ES6 제너레이터, 무한 개념 구현하기
ES6에서 도입된 제너레이터(Generator) 함수는 iterable을 생성하는 함수이다.
다음은 제너레이터를 생성하고, yield로 중단점을 설정하며, next()를 통해 실행한 예이다.
function* generator() {
console.log(1);
yield; // 중단점
console.log(2);
yield; // 중단점
console.log(3);
}
const gen = generator();
gen.next(); // 1 출력
console.log("첫 중단");
gen.next(); // 2 출력
console.log("다음 중단");
gen.next(); // 3 출력
다수의 yield를 여러개 설정하면 그 만큼 next로 yield를 건너 뛰어야 합니다.
function* generator() {
console.log(1);
yield 7; // 중단점
yield 5; // 중단점
yield 9; // 중단점
console.log(2);
yield 100; // 중단점
console.log(3);
}
const gen = generator();
gen.next(); // 1 출력
gen.next();
gen.next();
gen.next(); // 2 출력
yield 1, yield 2, yield 3은 yield* [1, 2, 3] 로 간략하게 작성할 수도 있습니다.
이를 이용해서 next를 호출할 때마다 무한히 숫자를 증가시키는 로직을 구현해보겠습니다.
while true로 묶어놓았으니 매번 next를 호출할 때마다 숫자가 증가한 결과물을 출력하게 됩니다.
이 방식은 어떤 action을 무수히 많이 날렸을 때도 saga가 계속 반응하게끔 만들어주는 데 사용되는 로직이기도 합니다.
(saga에서 제너레이터를 watchxxxx 로 짓는 이유입니다.)
function* generator() {
let i = 0;
while (true) {
console.log(i);
yield i++;
}
}
const gen = generator();
gen.next(); // 0
gen.next(); // 1
...
사실 제너레이터는 async/await가 등장하기 전에 사용되었던 기능입니다.
함수를 중간에 멈출 수 있다는 점 때문에 async/await 보다 편의성은 떨어지지만 중단점을 설정할 수 있다는 점에서 조금 더 강력합니다. 예를 들어 동작은 5번만 액션을 허용하고 다음 액션은 무시하도록하게 할 수도 있습니다.
// 계속 watching
while (true) {
yield take(ACTION);
}
// 5번만 action 작동
for (let i = 0 ; i< 5; i++) {
yield take(ACTION);
}
이런 점이 redux-saga는 async/await를 사용하지 않고 제너레이터를 사용하는 이유입니다.
여튼, 이제 아래와 같은 saga 코드에서 이제 yield가 의미하는 바를 알 수 있을 겁니다.
function* login() {
try {
yield call(loginAPI);
yield put({
type: LOG_IN_SUCCESS,
});
} catch (e) {
console.error(e);
yield put({
type: LOG_IN_FAILURE,
});
}
}
function* watchLogin() {
yield takeLatest(LOG_IN, login);
}
export default function* userSaga() {
yield all([fork(watchLogin)]);
}
참고한 글)
https://www.zerocho.com/category/ECMAScript/post/579b34e3062e76a002648af5
https://poiemaweb.com/es6-generator