자바스크립트에도 제너레이터라는 개념이 있다. 원하는 만큼 코드를 시작 또는 중지할 수 있는 함수이다. yield
로 값을 반환하고 기본적으로 일시중지되었다가, .next()
를 통해 yield
값 반환을 계속 이어갈 수 있다.
// 제너레이터 함수 (function*로 선언)
function* fruitList() {
yield 'Banana';
yield 'Apple';
yield 'Orange';
}
// 제너레이터 함수를 실행
const fruits = fruitList();
// 제너레이터 함수를 실행한 것을 가지고 next()로 yield값 호출
// value에 yield 값이 있는 한(= undefined가 아닌 한) done은 계속 false
fruits.next(); // { value: "Banana", done: false }
fruits.next(); // { value: "Apple", done: false }
fruits.next(); // { value: "Orange", done: false }
// 반환값을 모두 반환했는데도 또 next()를 호출하면 done이 true가 됨
fruits.next(); // { value: undefined, done: true }
제너레이터 실행을 시작한 후 각 next() 사이에서 일시중지되어 있다가 yield 값이 모두 반환되면 실행이 종료(done: true)된다.
for-of 반복문을 통해 제너레이터를 반복하고 결과값을 반환할 수 있다.
const fruitList = ['Banana', 'Apple', 'Orange', 'Melon', 'Cherry'];
// 제너레이터 반복문 함수 생성
function* loop(arr) {
for (const item of arr) {
yield `I like to eat ${item}s`;
}
}
// 제너레이터 함수 실행
const fruitGenerator = loop(fruitList);
// next()로 for-of문의 yield값 차례차례 반환
fruitGenerator.next(); // { value: "I like to eat Bananas", done: false }
fruitGenerator.next(); // { value: "I like to eat Apples", done: false }
// 특정 차례에 value만 출력할 수도 있음
fruitGenerator.next().value; // "I like to eat Orange"
.return() 메서드의 기능은 두 가지이다.
function* fruitList() {
yield 'Banana';
yield 'Apple';
yield 'Orange';
}
const fruits = fruitList();
// 아무 next() 없이 바로 return (종료 + 인자값 반환)
fruits.return(); // { value: undefined, done: true }
// - return 메서드에 인자로 아무 것도 전달하지 않아서 value에 undefined 반환
// 제너레이터 함수 생성 (try/catch 포함)
function* gen() {
try {
yield "Trying...";
yield "Trying harder...";
yield "Trying even harder...";
}
catch(err) {
console.log("Error: " + err);
}
}
// 제너레이터 함수 실행
const myGenerator = gen();
// next()로 yield 값 반환
myGenerator.next(); // { value: "Trying...", done: false }
myGenerator.next(); // { value: "Trying harder...", done: false }
// 오류 발생시켜 catch문으로 이동하게 함
myGenerator.throw("웁스..!");
// 두 가지가 반환된다.
// '웁스..!' -> catch문의 err 인자인 '웁스..!' 출력,
// { value: undefined, done: true } -> 오류가 났으므로 value에 undefined 반환 후 '종료'
// - '종료'하면 뒤에 yield 값이 남아 있어도 출력하지 않고 그대로 마침
제너레이터를 프로미스와 함께 사용하면 마치 동기인 것처럼 비동기 코드를 작성할 수 있다.
const myPromise = () => new Promise((resolve) => {
resolve("The value is ");
});
// 비동기 제너레이터 함수 생성 (프로미스 결과값을 반환)
function* gen() {
let result = '';
yield myPromise().then((data) => { result = data }); // { } 부분 = Promise
yield result + 2;
};
// 제너레이터 함수 실행
const asyncFunc = gen();
// next()로 첫 번째 yield 실행
const val1 = asyncFunc.next();
// 첫 번째 yield 값 출력
console.log(val1); // { value: Promise, done: false } -> Promise는 { result = data } 부분
// 프로미스가 완전히 완료('반환')되길 기다렸다가
// 반환된 프로미스(객체)의 value(= result)를 가지고(= then()) 두 번째 yield문 실행 (result + 2)
val1.value.then(() => {
console.log(asyncFunc.next()); // { value: "The value is 2", done: false }
});