티스토리 뷰
회사에서 redux-saga 상태 관리 라이브러리로 개발한 프로젝트가 있는데 generator 함수와 yield 키워드를 사용해서 개발을 했었습니다. 당시에는 개발 기한이 짧아 잘 이해하지 못하고 사용했었는데, 블로그에 글을 쓰다가 generator 함수와 yield 키워드와 관련된 내용이 나와서 이번에 공부하고 포스팅합니다!!
1. generator 함수와 yield
- ✅ function* 키워드로 함수를 정의(generator)하고 이 함수에 포함되어 있는 property 중에 next()를 호출하여, 함수 내에서 중지했던 yield 부분부터 재개합니다.
1-1. generator 함수
- function 뒤에 astrok(*)를 붙여서 generator 함수를 정의합니다.
- 제네레이터 함수는 일반 함수와 같이 함수의 코드 블록을 한 번에 실행하는 것이 아닌 함수 코드 블록의 실행을 일시 정지했다가 필요한 시점에 재시작할 수 있는 함수입니다.
- 마치 함수가 실행 중에 특정 부분에서 일시 정지된 것처럼 동작함으로써, 비동기 처리를 하도록 만들 수가 있습니다.
1-2. yield
- generator 함수 안에 yield 키워드를 사용한 곳들을 기준으로 코드를 일시 정지합니다.
1-3. next
- next 함수 호출하면 멈췄던 부분부터 실행을 이어서 하고 만약 다음 yield가 있으면 해당 부분에서 일시 정지합니다.
- generator 함수에서 반환한 데이터를 generator라고 부르는데, generator의 next함수를 실행하면 {value, done} 타입의 객체 데이터를 반환합니다.
- next()를 실행하는 이유는 generator는 반복적인 iterator를 사용하는 구조이기 때문입니다.
* iterator(반복자) : 객체 지향 프로그래밍에서 배열이나 그와 유사한 자료구조의 내부의 요소를 순환하는 객체
function* call() {
console.log('실행 1');
yield 'type1';
console.log('실행 2');
yield 'type2';
console.log('실행 3');
yield 'type3';
}
// 방법 1
let calling = call();
console.log(calling.next());
console.log(calling.next());
console.log(calling.next());
console.log(calling.next());
//
// 방법 2
for(let result of call()){
console.log(result);
}
//
1. 제너레이터 함수 call을 정의하고, 호출하여 사용해봤습니다.
2. next 함수를 통해 다음 함수를 실행하면서 호출해본 결과 yield 문을 만날 때까지 실행되었다 멈추는 것을 확인할 수 있습니다.
3. value : yield로 반환했던 값, done : 함수의 종료 여부 } 형식으로 리턴 값이 반환되는 것을 확인할 수 있습니다.
* generator는 반복적인 iterator를 사용하는 구조이기 때문에 for문 활용이 가능합니다.
심화 내용이 궁금하다면 Click!!
sub 1) 제너레이터 종료하기
- return 문
- return이 호출되면 value에는 return의 인자가 할당되고, done은 true가 됩니다.
function* increment() {
let i = 0;
while (true) {
yield i++;
}
}
const withReturn = increment();
console.log(withReturn.next()); // {value: 0, done: false}
console.log(withReturn.next()); // {value: 1, done: false}
console.log(withReturn.next()); // {value: 2, done: false}
console.log(withReturn.next()); // {value: 3, done: false}
console.log(withReturn.return(100)); //{value: 100, done: true}
- return문 + try / finally
- return이 호출되었을 때 제너레이터 함수의 코드가 try / finally 안에 있으면, 종료되지 않습니다. return 이후 finally 블록의 yield 표현식이 실행되며, 종료될 때는 reuturn에 전달된 값으로 종료됩니다.
function* increment() {
yield 1;
try {
yield 2;
yield 3;
yield 4;
} finally {
yield 5;
yield 6;
}
yield 7;
}
const withReturn = increment();
console.log(withReturn.next()); // {value: 1, done: false}
console.log(withReturn.next()); // {value: 2, done: false} => 이 시점에 try/finally 문으로 들어옴
console.log(withReturn.return(100)); // {value: 5, done: false}
console.log(withReturn.next()); // {value: 6, done: false}
console.log(withReturn.next()); // {value: 100, done: true}
sub 2) yield*
- yield에 *를 붙여 사용하면 yield* 에 표현된 이터러블 객체를 순회하며 결과값을 반환합니다.
function* increment() {
let a = 10;
yield a;
yield* [1, 2, 3].map((dr) => dr * a);
let b = 20;
yield b;
yield* [4, 5, 6].map((dr) => dr * b);
}
for (let result of increment()) {
console.log(result);
}
/* 호출결과
10
10
20
30
20
80
100
120
*/
2. generator + yield vs async + await
- ✅ generator 함수는 ES2015에 나와 일시 중지했다가 필요한 시점에 재시작하는 특징을 통해 비동기 처리에 유용하게 사용되었나, ES2017에 async/await을 이용하여 비동기 처리가 가능해짐에 따라 존재감이 낮아지고 있습니다.
- generator + yield의 경우 generator의 리턴 값이 iterator로, yield에서 작업이 중단되면 next()를 통해 다음 작업으로 넘어가는 불편함이 있습니다.
- async + await의 경우 await에 promise가 오면 resolve가 반환될 때까지 기다렸다가 다음 작업으로 넘어가기 때문에 별도의 처리를 해 줄 필요가 없습니다.
=> 비동기 코드를 기다리는 즉, 동기식으로 동작하는 것처럼 보이는 방식으로 작성하기 위한 훌륭한 방법은 async + await입니다! 하지만 한 번에 여러 개의 promise를 기다릴 수 없기 때문에, 여러 개의 promise를 동시에 처리를 하기 위해서는 promise 함수를 사용해야 합니다.
3. redux-saga / effects 함수에서의 generator 함수와 yield 사용
- ✅ redux는 ES6의 generator 함수를 기반으로 구현되었습니다. function* 키워드로 함수를 정의하고, 이 함수를 호출하는 행위는 redux-saga/effects에 위임시키고, redux에서 제공하는 action을 사용하여 함수 내 yield 문을 차례로 실행시키는 것입니다. 이때 redux에서 함수 내의 yield를 알아서 차례로 실행시켜주기 때문에 next()를 계속해서 호출할 필요는 없습니다.
3-1. redux-saga / effects 함수
- redux-saga / effects 함수로는 all, call, put, takeLatest 등이 있습니다.
all : 비동기 처리가 필요한 함수들을 배열 행태로 넣어서 동시에 병행 처리를 수행하는 함수
call : 비동기 처리가 필요한 함수들을 실행하는 함수 (주로 ajax, http, delay 등의 함수를 처리)
put : action을 dispatch 하는 함수
takeLatest : 기존에 진행 중이던 작업이 있다면 취소하고 가장 마지막으로 실행된 작업을 수행하는 함수 - redux-saga / effects 함수들을 사용하게 되면, function* 함수를 호출할 때 next()를 통해 호출하는 것이 아닌, action이라는 것을 통해 호출합니다. action을 통해 function*을 한 번만 호출하면 내부에 있는 여러 yield 들을 next() 호출 처리 없이 차례로 호출을 해줍니다. 이러한 점이 비동기 처리에 대한 코드를 보다 쉽체 처리할 수 있게 해 줍니다.
🤔 정리 !
- generator함수는 function* 키워드로 정의, yield 키워드를 만나면 일시 정지, next()를 호출하면 일시 정지된 부분부터 재개 ] 이는 마치 함수가 특정 부분에서 일시 정지된 것처럼 동작하므로 비동기 처리를 하도록 만들 수가 있다!
- 비동기 처리를 하는 더 좋은 방법은 async + await을 사용하는 것!
- redux-saga/effects 함수에서는 action을 통해 generator 함수를 호출하면 함수 내 yield 부분 들을 자동으로 차례로 호출!
🔗 참고한 글
'JavaScript' 카테고리의 다른 글
시맨틱 웹(Semantic Web) ? (2) | 2022.07.16 |
---|---|
JS - 구조 분해 할당 문법 ? (0) | 2022.07.15 |
JS - JSON ? (0) | 2022.07.09 |
JS - 호이스팅 (Hoisting) ? (0) | 2022.07.05 |
JS - Lodash ? (3) | 2022.07.04 |
- Total
- Today
- Yesterday
- 1급 시민
- Virtual Scroll
- 가상스크롤
- redirects
- useRef
- typescript
- 매겨변수와 인자
- programmers
- 렌더링 속도 개선
- 자바스크립트 비동기 동작원리
- 1급 객체
- zustand
- vue
- next.js 환경변수
- 1급 함수
- react
- debouncing
- Next.js
- 시맨틱 웹
- 자바스크립트 동작원리
- array
- 타입스크립트
- 목표 일기
- 함수형 컴포넌트
- 호이스팅
- React로 쓰로틀링 디바운싱 구현
- next.js에 .gitignore가 적용되지 않을 때
- javascript
- rewrites
- redux
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |