❓ redux-saga를 익히고 있는 도중 generator에 대해서 반드시 이해하고 넘어가야 할 것같아서 공부하고 있다.
블로그에 간단히 정리하며 마무리 지을 생각이다.
Generator란?
비동기 작업을 동기적으로 표현하기 위한 방법 중 하나이다.
그 외 Promise, async/await가 있다.
function*
과 같이 *
를 function
뒤에 붙이면 제너레이터가 된다. yield
를 입력하면 제너레이터를 중지 및 재개 할 수 있다.next
를 통해 제너레이터를 실행할 수 있다. function* sayHello() {
console.log('제너레이터 시작')
yield '안녕';
console.log('첫번째 yield의 비동기 작업 끝낸 후 실행');
yield '나는 Winney야'
console.log('두번째 yield의 비동기 작업 끝낸 후 실행');
yield '앞으로 잘 지내자'
console.log('제너레이터 끝');
};
const gen = sayHello();
gen.next(); // 1번
gen.next(); // 2번
gen.next(); // 3번
gen.next(); // 4번
작성 방법을 알았으니 제너레이터가 어떻게 실행되는지 파악해야 할 차례이다.
다음은 위의 예시를 실행한 결과이다.
// 1번 gen.next() 실행
'제너레이터 시작'
{ value: '안녕', done: false }
// 2번 gen.next() 실행
'첫번째 yield의 비동기 작업(안녕) 끝낸 후 실행'
{ value: '나는 Winney야', done: false }
// 3번 gen.next() 실행
'두번째 yield의 비동기 작업(나는 Winney야) 끝낸 후 실행'
{ value: '앞으로 잘 지내자', done: false }
// 4번 gen.next() 실행
'제너레이터 끝'
{ value: undefined, done: true }
여기서 중요한 부분은 next
를 이용해서 제너레이터를 작성했을 때 해당 제너레이터의 실행은 yield
작성한 부분에서 멈춘다는 것이다.
이후 다시 next
로 제너레이터를 실행했을 때 이전 yield
의 다음 줄에서부터 실행되어 그 다음 `yield
에서 제너레이터가 멈추는 것은 반복한다는 것이다.
즉, 위에서 말한 yield
를 입력하면 제너레이터를 중지 및 재개 할 수 있다.는 의미의 yield
는 코드가 어딘가에서 멈춰서 비동기작업을 하고 해당 작업이 끝나면 다시 제너레이터가 실행 될 수 있게 하는 키워드라는 의미이다.
그리고 위의 실행 결과를 통해 yield
는 객체형태로 value
와 done
을 반환하는 것을 알 수 있다.
value
: yield
실행 결과의 반환 값done
: 제너레이터가 완전히 종료되었는지를 확인하는 값때문에 4번째 gen.next()
의 yield
반환값을 확인하면 { value: undefined, done: true }
를 확인 할 수 있다.
이전 3번째 yield
가 마지막으로 실행되며 멈춘 다음 줄에서 4번째 gen.next()
가 실행되므로 console.log('제너레이터 끝');
가 실행된 후 제너레이터가 끝난다.
그렇기에 yield
가 없어서 반환값이 없으므로 value
는 undefined
가 나오고 제너레이터는 완전히 종료되었으므로 done
은 true
가 된다.
제너레이터는 기본적으로 비동기 작업을 동기적으로 표현하므로서 가독성있게 표현하기 위한 것이다. (하지만 난 아무리 봐도 동기적으로 보인다... 물론 비동기 작업이 있다는 것을 알지만.. 😂)
function* countNaturalNum() {
let num = 0;
while(true) {
num += 1
yield num;
}
}
const gen = countNaturalNum()
gen.next(); // { value: 1, done: false }
gen.next(); // { value: 2, done: false }
gen.next(); // { value: 3, done: false }
gen.next(); // { value: 4, done: false }
만약에 함수였다면 무한루프를 돌았을 것이지만 제너레이터를 사용하면서 자연수를 표현할 수 있게 되었다.
❗ 예시와 결과가 나오는데 예시만 보고 꼭 결과를 예측해볼 것
next
의 문법 :gen.next(value)
next
에는 매개변수가 있고value
는 제너레이터에 전달된다.
그리고 이미 배웠다시피 next
는 value
와 done
프로퍼티를 가진 객체를 반환한다.
function* getArg() {
while(true) {
let value = yield null;
console.log(value);
}
}
const gen = getArg()
gen.next(1); // 1번
gen.next(2); // 2번
// 1번
{ value: null, done: false }
// 2번
2
{ value: null, done: false }
yield null
에서 멈추었기 때문에 yield
의 결과인 null
이 value: null
로 나타났다. yield
다음 줄부터 실행되기 때문에 console.log(value)
가 실행된다. 당연히 next(2)
에서 2
가 인자로 들어갔기 때문에 2
가 출력되고 while반복문이기에 다시 돌아가서 yield null
이 실행되어 value: null
가 나타난다. function* getArg() {
returnedYield = yield 'foo';
yield returnedYield;
}
const gen = getArg()
gen.next(1); // 1번
gen.next(2); // 2번
// 1번
{ value: 'foo', done: false }
// 2번
{ value: 2, done: false }
제너레이터를 익히며 제일 어려웠던 부분이다!!
💥 중요
분명gen.next(1)
에서 1을 전달했는데 왜value
는'foo'
로 나올까?
returnedYield
는 어디로 갔나?
- mdn
yield
문법 :[rv] = yield [expression];
여기서 중요한 부분은[expression]
이다. 우리가 알아야 할 것은next(value)
에서 저value
매개변수가yield
에게 전달되고 그 자리가[expression]
자리라는 것이다. (yield
오른쪽)
때문에gen.next(1);
를 통해서1
을yield 'foo'
에 전달하므로서yield 1
되었다.
하지만yield 1
이returnedYield
에 전달되는 것은 다음yield
가 실행될 때이다.
그렇기에 1번 결과에서 우리는value: 'foo'
를 확인 할 수 있고 2번결과에서는 이미next(2)
를 통해yield
가2
를value
로 가진 상황에서returnedYield
를 반환하게 되므로{ value: 2, done: false }
과 같은 결과가 나온 것이다.
참조 StackOverFlow : https://stackoverflow.com/questions/37354461/how-does-generator-next-processes-its-parameter
개인적으로 next
에 인자를 전달한 값을 예측하는게 제일 어려운 부분이었다.
iterator
와 iterable
에 관해서도 작성하고 싶지만 다음에 전반적으로 다루어 보도록 하고 제너레이터에 관한 내용을 마무리한다,