Sync vs Async
Sync
- 실행결과를 return '결과'로 반환
- 프레임이 적재된 순서대로 실행하는것
Async
- 콜백(전달된 함수)을 통해 리턴값을 반환
- 콜백함수의 위치에 따라 goto 처럼 이동하게 위/아래로 이동하기 떄문에 디버깅하기 어려움(코드가 날라다닌다)
- Sync 로직 플로우(위에서 아래 / 좌에서 우)가 익숙하기 때문에 이해하기 어렵다
Generator
Breaking Block
- 블록이 다 실행시키전(결과를 반환)까지 정지를 시킬 수 없다
- break를 통해 블록을 중지 시킬 수는 있지만, 우리가 원하는게 아니다
- Generator Suspend를 통해 Flow를 정지시킬 수 있다(Yield 전)
- next() 함수를 통해 CoRoution(정지된 상태(지역변수)로 이동)를 실현 ⇒ 다시 Flow 진행
const infinity = (function* () {
let i = 0
while (true) yield i++
})()
console.log(infinity.next())
console.log(infinity.next())
Blocking Evasion
- Time Slice Manual
- 로직과 알고리즘이 함께 있어 어느 부분이 변경이 된다면 손이 많이 간다
- 클로져로 인해(자유변수 사용) 로직을 이해하는 것이 힘들다.
// async nonBlock
const looper = (n, f, slice = 3) => {
let limit = 0,
i = 0 // 자유변수
const runner = (_) => {
while (i < n) {
if (limit++ < slice) f(i++)
else {
limit = 0
requestAnimationFrame(runner)
break
}
}
}
requestAnimationFrame(runner)
}
looper(10, console.log)
-
Time Slice Manual Using Generator
- Coroutine으로 인해 상태를 유지하기 때문에 지역변수로 사용하여 이해가 쉽다.
- Flow Control과 Logic을 분리하여 유지보수에 용이하다(관심사 분리)
//데이터 로직
const loop = function* (n, f, slice = 3) {
let limit = 0,
i = 0 // 지역변수 (제네레이터의 지역변수)
while (i < n) {
if (limit++ < slice) f(i++)
else {
limit = 0
yield
}
}
}
//실행기
const executor = (iter) => {
const runner = (_) => {
iter.next()
requestAnimationFrame(runner)
}
requestAnimationFrame(runner)
}
executor(loop(10, console.log))
-
Why use Generator
- 제어 구조를 분리해서 재활용 할 수 있다(같이 있으면 재사용 불가)
- Generator Block Braking 속성을 이용하면
- 실행기와 핵심로직을 분리
- 자유변수보다는 지역변수를 이용하여 로직을 단순화
Generator + Async + Executor(컨슈머 용법 활용)
-
데이터 로직
const profile = function*(end, next, r){ const userid = yield $.post('member.php', {r}, next); let added = yield $.post('detail.php', {userid}, next); added = added.split(","); end({userid, nick:added[0], thumb:added[1]});
- profile 함수가 호출이 되면 yield까지 실행이 된다($.post()를 호출하고 나서 결과값을 가진 상태)
- 하지만 userid로 할당이 되지 않는다(next()를 호출하기 전까지)
-
실행기
const executor = (end, gene, ...arg) => { const next = (v) => iter.next(v) const iter = gene(end, next, ...arg) iter.next() } executor(console.log, profile, 123)
- $.post가 결과값을 next로 넘겨주게 된다.
- 값을 yield로 할당하면서 next()를 호출한다. 다음 yield까지 실행하게 된다.
-
좋은점?
- 콜백지옥에서 탈출
- 모든게 다 지역변수로 관리된다
- 모든게 다 동기로직으로 변경되었다.
Promise
Passive Async Controll(수동적)
-
콜백을 보낼 수는 있지만 언제 올지는 모른다.
-
병렬로 진행될 수 있음에도 불구하고 순서 때문에 한번에 하나 씩 진행
- useid를 받아와야지 프로필 정보를 받아올 수 있다.
// 언제 결과가 올지 몰라 에러가 발생할 수도 있다 let result $.post(url1, data1, (v) => { result = v }) $.post(url2, data2, (v) => { result.nick = v.nick report(result) })
-
순서가 있는 콜백은 어쩔 수 없다. 현재 이 방법 뿐이다
Active Async Controll(능동적)
- 호출하기 전까지는 발동하지 않는다(Promise)
- Promise는 then을 호출해야지만 결과를 얻는다
- 요청이 완료되지 않으면 결과를 못받는다.
- 중요한 점은 then을 호출하기 전까지는 상태가 완료되도 실행되지 않는다.
Why Promise
- 실제적인 비동기 행위와 콜백의 처리를 완전히 분리 할수 있다.
- 프로미스가 어떤 작업의 상태인지 우리 관심에서 멀어졌다.(then만 기억)
- 병렬/병행성 작업을 진행하여 순서가 없는 작업에 대해서 처리 할 수 있다.
- promise1, promise2는 동시에 요청이된다. 그래서 all,race, some이 나온것임
- But!! 순서 잇는 로직은 적용하지 못하고 callback으로 진행, 처음부터 구조를 잘 짜야한다.
- 우리는 흐름제어만 신경쓰자!!
let result;
const promise = new Promise(r => $.post(url1, data1, r));
promise.then(v => {
return = v;
})
const promise1 = new Promise(r => $.post(url1, data1, r));
const promise2 = new Promise(r => $.post(url2, data2, r));
promise1.then(result => {
promise2.then(v => {
result.nick = v.nick;
report(result);
})
});
Generator + Promise
- Promise 또한 비동기적인 작업 일 수 밖에 없다
- next()가 사라지면서 상태에 대한 관심이 분리되어 자기 참조 무결성이 더욱더 강해졌다.
- 초반에 생성을 할 수 있는 데이터를 일체 받고(병렬) 나중에 조립하는 로직만 프로미스로 하자
const profile = function*(end, r){
const userid = yield new Promise(res => $.post('member.php', {r}, res));
let added = yield new Promise(res => $.post('detail.php', {userid}, res));
added = added.split(',');
end({userid, nick:added[0], thunb:added[1]});
const executor = (end, gene, ...arg) => {
const iter = gene(end, ...arg);
const next = ({value, done}) => {
if(!done) value.then(v => next(iter.next(v));
};
next(iter.next());
};
executor(console.log, profile, 123)
Async Await
- 제네레이터의 실행기가 언어적으로 내장되어 있어서 사용된다
- 하지만 동기적으로 작동하기 때문에 병렬안되나, await promise.all(()로 진행할 수 있다.
- AWAIT PROMISE = SYNC
- 코드가 어떻게 줄어졌는지? 어떤게 내장되어 있는지? 고민해보자.
const profile = async function(end, r){
const userid = await new Promise(res => $.post('member.php', {r}, res));
let added = await new Promise(res => $.post('detail.php', {userid}, res));
added = added.split(',');
end({userid, nick:added[0], thunb:added[1]}
);
profile(console.log, 123)
추가사항
- 자바스크립트 함수는 내부의 문법 오류가 없다면 모든 변수를 실행 시점에 평가한다.
- const / let을 사용하면 tdz를 생성해서 호이스팅을 방지한다.
- 콜백에 대해서 순서없는 프로그래밍을 짜라 리엑티브 프로그래밍