✈️

[JS] 제너레이터


제너레이터

제너레이터(generator)를 사용하면 여러 개의 값을 필요에 따라 하나씩 반환(yield)하는 함수를 만들 수 있다.

제네레이터는 '제네레이터 함수'라 불리는 특별한 문법 구조가 필요하다

1function* generateSequence() {
2 yield 1;
3 yield 2;
4 return 3;
5}
6let generator = generateSequence();
7console.log(generator.next());
8// -> {value: 1, done: false}
9console.log(generator.next());
10// -> {value: 2, done: false}
11console.log(generator.next());
12// -> {value: 3, done: true}

제너레이터 함수를 호출하면 실행을 처리하는 특별한 객체인 '제너레이터 객체'가 반환된다.

next()는 제너레이터의 주요 메서드다. next()를 호출하면 가장 가까운 yield문을 만날 때까지 실행하고 멈춘다. 이후, yield <value>문을 만나 value를 반환한다.

next()는 산출값인 value와 함수 코드 실행이 끝났는지 알려주는 불린값 done을 가진 객체를 반환한다.

제너레이터가 종료되었을 떄 generator.next()를 여러번 호출해도 객체 {value: undefined, done: true}만 계속 반환된다.

제너레이터와 이터러블

next()를 보면 알 수 있듯이, 제너레이터는 이터러블이다. 따라서, for..of 반복문을 사용하는 등 이터러블로 가능한 모든 것을 제너레이터로 수행할 수 있다.

1let generator = generateSequence();
2for (const value of generator) {
3 console.log(value);
4}
5// -> 1, 2

마지막 값이 출력되게 하기 위해서는 returnyield로 바꿔야 한다. for..ofdone: true이면 마지막 값을 무시하기 때문이다.

'yield'를 사용해 제네레이터 안/밖으로 정보교환

yield는 결과를 바깥으로 전달할 뿐만 아니라 제너레이터 안으로 전달할 수 있다.

값을 안으로 전달할 때는 next(arg)arg 인수를 전달하면 된다. argyield의 결과가 된다.

1function* gen() {
2 let result = yield "2 + 2 = ?";
3 console.log(result);
4}
5
6let generator = gen();
7let question = generator.next().value;
8generator.next(4);
9// -> 4

async 제너레이터

이터러블에 비동기적 작업을 다루기 위해 Symbol.asyncIterator가 있듯이, 제너레이터에서는 async를 사용할 수 있다.

제너레이터 함수 앞에 async를 붙이면, 본문에서 await를 사용할 수 있다.

1async function* generateSequence(start, end) {
2 for (let i = start; i <= end; i++) {
3 await new Promise(resolve => setTimeout(resolve, 1000));
4 yield i;
5 }
6}
7(async () => {
8 let generator = generateSequence(1, 5);
9 for await (let value of generator) {
10 console.log(value); // 1, 2, 3, 4, 5
11 }
12})();

참조:
ko.javascript.info
https://poiemaweb.com/es6-generator