# 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