JavaScript ES6+ 중급(4) Generator
Updated:
이 글은 정재남 개발자님의 인프런 강의인 JavaScript ES6+ 제대로 알아보기 중급편을 정리한 내용입니다.
1. 개념
generator는 함수 표시(function) 앞에 *가 붙는 녀석이다. 이 녀석은 next() 메소드를 호출하면 yield(넘겨주다, 양도하다) 키워드를 만나기 전까지를 실행한다. 선언과 간단한 활용은 아래와 같이 한다. next() 메소드를 호출하면 객체에 value와 done 프로퍼티가 있는 것을 알 수 있다.
function* gen() {
console.log(1);
yield 1;
console.log(2);
yield 2;
console.log(3);
yield 3;
}
const gen = gene();
gen.next();
// 1
// {value: 1, done: false}
gen.next();
// 2
// {value: 2, done: false}
gen.next();
// 3
// {value: 3, done: false}
gen.next();
// {value: undefined, done: true}
객체 내부에 축약형 함수를 선언할 경우 *은 변수명 앞에 둔다. class에서도 마찬가지다.
const obj = {
gen1: function* () {.
*gen2 () { yield }
}
class A {
*gen () { yield }
}
2. iterator로서의 generator
const obj = {
a: 1,
b: 2,
c: 3,
*[Symbol.iterator] () {
for(let prop in this) {
yield [prop, this[prop]];
}
}
}
console.log(...obj);
// ["a", 1], ["b", 2], ["c", 3]
for(let p of obj) {
console.log(p);
}
// ["a", 1]
// ["b", 2]
// ["c", 3]
객체에 iterator를 적용하기 위해서 Symbol.iterator가 객체를 반환해야 되고… 그 객체는 next() 메소드를 반환해야 되고… done 프로퍼티를 반환해야 한다 등의 규칙을 적용해야 했지만, generator를 활용해 yield를 적절히 사용하면 복잡하게 구현할 필요성이 줄어들게 된다.
yield 키워드 뒤에 *가 붙어 있고, 그 뒤에 iterator 요소가 오면, iterator의 내부 요소들을 하나씩 반환한다.
function* gen() {
yield* [1, 2, 3];
yield* 'abc';
}
const gene = gen();
gene.next();
// {value: 1, done: false}
gene.next();
// {value: 2, done: false}
gene.next();
// {value: 3, done: false}
gene.next();
// {value: 'a', done: false}
// ...
yield 뒤에 generator를 사용할 수도 있다. 그러면 참 복잡해진다. 큼…🤣
3. yield와 값의 할당
예제부터 보자.
function gen() {
let first = yield 1;
console.log(first);
let second = yield first + 2;
console.log(second);
}
const gene = gen();
gene.next();
// {value: 1, done: false)
gene.next();
// {value: NaN, done: false}
위 예제에서 첫번째 yield를 만났을 때 value 1을 반환하는데 마치 이 값은 first 변수에 할당될 것만 같다. 하지만 그렇지 않다. yield는 value와 done의 객체를 반환하지만 변수에 값을 할당하지 않는다. 그래서 두 번째 yield를 했을 때 반환되는 value 값은 NaN이 나오게 된다. first에 해당하는 값을 할당하고자 한다면 두 번째 next 메소드를 호출할 때(gene.next(10)) 인자에 값을 넣어주면 된다.
4. 비동기 처리 예시
아래 예제는 1000번째 이후 유저들을 가져와서 4번째(1004), 6번째(1006) 유저들의 정보를 가져오는 것이다. 천천히 코드를 음미하면서 이해해 보자 😳. 특히 generator를 인자로 넘기고, fetchWrapper에서 then 이후에 다시 next를 호출하는 것을 이해해 보자!
const fetchWrapper = (gen, url) => fetch(url)
.then(res => res.json())
.then(res => gen.next(res));
function getNthUserInfo() {
const [gen, from, nth] = yield;
const req1 = yield fetchWrapper(gen, `https://api.github.com/users?since=${from || 0}`);
const userId = req1[nth - 1 || 0].id;
console.log(userId);
const req2 = yield fetchWrapper(gen, `https://api.github.com/user/${userId}`);
console.log(req2);
}
const runGenerator = (generator, ...rest) => {
const gen = generator();
gen.next();
gen.next([gen, ...rest]);
}
runGenerator(genNthUserInfo, 1000, 4);
runGenerator(genNthUserInfo, 1000, 6);
Leave a comment