# Async Generator
ES2017 의 비동기함수가 도입되기 전, generator 가 비동기 프로그래밍을 위해 널리 사용되었다고한다.
일반함수
- (0, 1)개의 값만 반환
# 일반 Generator
- await 사용 불가 
- redux-saga 에서도 Generator 를 사용하고 있다. 
- 동기적 문법 
- 여러 개의 값을 필요에 따라 하나씩 yield 반환 한다. 
- 함수를 잠수 멈춰둘 수 있는 generator 의 특징을 이용해 비동기 프로그래밍을 위해 사용되기도 한다. - const infinity = (function*() { let i = 0; while(true) yield i++; })(); console.log(infinity.next());1
 2
 3
 4
 5- generator 가 아니며 while 이 무한으로 반복되고 있을 때
- SyncFlow 에서는 CPU 의 blocking 이 계속 일어남
 
- generator 인 경우 while 문을 yield 를 통해서 탈출 가능
 
- generator 가 아니며 while 이 무한으로 반복되고 있을 때
- 함수의 재개를 프로그래머가 직접 제어할 수 있다. 
- Suspend: Sync Flow 를 중단할 수 있다. 
# nbFor 함수(일반함수) 를 generator 함수로 만들기
nbFor: load 값 조절을 통한 nonBlocking 함수.
const nbFor = (max, load, block) => {
  let i = 0;
  const f = time => {
    let curr = load;
    while(curr-- && i < max) {
      block();
      i++;
    }
    if (i < max - 1) requestAnimationFrame(f);
  };
  requestAnimationFrame(f);
};
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
generator 를 통해 requestAnimationFrame 와 같은 async 함수를 없애보자
- 재귀호출 위치를 추적하지 않아도 된다.
- syncFlow 로직이기 때문에 더 이해하기 쉽다.
- async 함수 단점이 없어진다.
- 어휘공간을 유지할 수 있어 복잡한 스코프를 고려하지 않아도 된다.
 
const gene = function*(max, load, block) {
    let i = 0, curr = load;
    while(i < max) {
        if(curr--) {
            block();
            i++;
        } else {
            curr = load;
            yield;
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 외부에서 제어할 수 있는 권한이 생겼다.
const nbFor = (max, load, block) => {
    const iterator = gene(max, load, block);
    const f = _ => iterator.next().done || requestAnimationFrame(f);
    requestAnimationFrame(f);
}
1
2
3
4
5
2
3
4
5
정리해 보자면
- 비동기 스타일의 콜백 loop 를 쓰는 것보다 동기적인 코드로 작성하기 편하다
- 실행부분과 Loop 의 관심사 분리
# Promise yield
Promise 자체는 즉시 yield 된다.
const gene2 = function*(max, load, block) {
    let i = 0;
    while (i < max) {
        yield new Promise(res => {
            let curr = load;
            while (curr-- && i < max) {
                block();
                i++;
            }
            // frame 단위로 resolve 실행
            requestAnimationFrame(res);
        })
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
Promise 가 resolve 되는 순간에 iterator.next() 가 된다.
const nbFor = (max, load, block) => {
    const iterator = gene2(max, load, block);
    const next = ({ value, done }) => done || value.then(v => next(iterator.next()));
    next(iterator.next());
}
1
2
3
4
5
2
3
4
5
load 를 없애 버린 다음 코드는 block 일까 non block 일까?
const gene2 = function*(max, block) {
    let i = 0;
    while (i++ < max) {
        yield new Promise(res => { block(); res(); })
    }
}
1
2
3
4
5
6
2
3
4
5
6
- Promise Jobs 때문에 cpu 가 멈추지는 않는다.
- 마이크로 태스크 micro task - 하나의 프레임(애니매이션) 안에서 다시 비동기 타이밍을 나누어주는 역할
- 한 프레임을 잡아두고 마이크로 태스크(promise job)를 실행하는데 해소될 때까지 프레임이 넘어가지 않는 현상이 발생한다.
- script timeout 이 걸리지 않기 때문에 브라우저가 죽지 않는다.
 
nb 가 10000번이 찍혀야 f 가 찍힌다.
nbFor(10000, _ => console.log("nb"));
const f = _ => {
    console.log('f');
    requestAnimationFrame(f);
};
requestAnimationFrame(f);
1
2
3
4
5
6
2
3
4
5
6
다음의 경우 nb 가 어느정도 찍히다가 프레임이 넘어간다.
const f = _ => {
    console.log('f');
    requestAnimationFrame(f);
};
requestAnimationFrame(f);
nbFor(10000, _ => console.log("nb"));
1
2
3
4
5
6
2
3
4
5
6
requestAnimationFrame 와 같이 시간에 관련된 비동기 함수를 time 함수라고 부르겠다.
promise 안의 time 함수
const gene2 = function* (max, block) {
    let i = 0;
    while (i++ <max) yield new Promise(res => {
        block();
        time(res);
    })
}
1
2
3
4
5
6
7
2
3
4
5
6
7
time 함수의 분리
const gene2 = function* (max, block) {
    let i = 0;
    while (i++ <max) yield new Promise(res => {
        block();
        res();
    })
};
const nbFor = (max, block) => {
    const iterator = gene2(max, block);
    const next = ({ value, done }) => done || value.then(v => time(_ => next(iterator.next())));
    next(iterator.next());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
# 제너레이터에 비동기 동작 추가
한번 콘솔로 돌려보자. 일반 제너레이터 함수와의 동작이 비교될 것이다.
모르겠다.
async function* generateSequence(start, end) {
  for (let i = start; i <= end; i++) {
    // await를 사용할 수 있습니다!
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i;
  }
}
(async () => {
  let generator = generateSequence(1, 5);
  for await (let value of generator) {
    alert(value); // 1, 2, 3, 4, 5
  }
})();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
# Reference & Comment
- https://gitlab.com/siots-study/topics/-/wikis/asyncronous
- https://www.youtube.com/watch?v=JaHlR1IGLN8&list=PL7jH19IHhOLMmmjrwCi7-dMFVdoU0hhgF
- https://helloworldjavascript.net/pages/285-async.html
- https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
- https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
- https://ko.javascript.info/async
- https://ko.javascript.info/generators-iterators
- https://www.bsidesoft.com/8325
- https://www.bsidesoft.com/6037