함수의 실행을 중간에 멈췄다가 재개할 수 있는 기능이다.
function* fn() {
console.log(1);
yield 1;
console.log(2);
yield 2;
console.log(3);
console.log(4);
yield 3;
return "finish";
}
const a = fn();
다음과 같이 제너레이터 함수는 function 키워드 옆에 *
을 붙여줍니다.
yield 키워드 부분에서 함수를 멈출 수 있습니다.
a 에는 fn() 실행을 통한 반환된 제너레이터 객체가 담겨져 있고 ,
제너레이터 함수인 next() 를 통해 결과를 확인해보면
a.next()
출력 : 1
상태 : {value : 1 , done: false}
a.next()
출력 : 2
상태 : {value : 2 , done: false}
...
a.next()
출력 :
상태 : {value : finish , done : true}
이 때 value 는 yield 옆에 붙어 있는 값이고 , done 은 해당 함수가 완료되면 true, 완료되지 않은 경우엔 false 를 리턴합니다.
위에서 예제로 설명한것처럼 가장 가까운 함수를 실행해준다
next() 함수를 실행하다가 . return("??") 를 실행하면
{value : ?? , done : true} 상태로 변하게 된다.
function* fn() {
try{
console.log(1);
yield 1;
console.log(2);
yield 2;
console.log(3);
console.log(4);
yield 3;
return "finish";
} catch(e){
console.log(e);
}
}
const a = fn();
마찬가지로 next() 함수를 실행하다가 throw(new Error('err')) 를 실행하면
{value : undefined , done : true} 상태로 변하게 된다.
Generator 는 iterable 하다.
- Symbol.iterator 메서드가 있다.
- Symbol.iterator 는 iterator 를 반환해야 한다.
iterator
- next 메서드를 가진다
- next 메서드는 value 와 done 속성을 가진 객체를 반환한다
- 작업이 끝나면 done 은 true 가 된다.
--> Generator 는 iterator 이면서 iterable 하다
: 배열 또한 여러 가지 요소가 순차적으로 들어있으므로 iterable 하지 않은가 ?
arr = [1,2,3,4,5] 와 같은 배열이 있다고 하자.
배열을 살펴보면 Symbol.iterator 메서드가 들어있다.
따라서const it = arr[Symbol.iterator]();
를 사용 하면it.next()와 같이 iterator 함수를 사용할 수 있다.
따라서 배열과 마찬가지로 Generator 또한
function* fn() {
yield 1;
yield 2;
yield 3;
}
const a = fn();
for (let num of a){
console.log(num); // 1, 2 , 3
}
다음과 같이 for of 반복문을 이용하여 generator 를 출력할 수 있다.
: 문자열도 마찬가지로 Symbol.iterator 메서드가 들어있다.
따라서 문자열도 배열이나 generator 처럼 iterable 한 것을 알 수 있다.
function* fn(){
const num1 = yield "첫번째 숫자를 입력하세요.";
console.log(num1);
const num2 = yield "두번째 숫자를 입력하세요.";
console.log(num2);
return num1 + num2;
}
const a = fn();
a.next()
-> {value : "첫번째 숫자를 입력하세요." , done:false}
a.next(2)
2
-> {value : "두번째 숫자를 입력하세요." , done:false}
a.next(4)
4
-> {value : 6 , done:true}
generator 는 값을 미리 만들어두지 않고 필요한 순간에만 연산을 하여 값을 주기 때문에 메모리 측면에서 조금 더 효율적이라고 볼 수 있다.
function* fn(){
let index = 0;
while(true) {
yield index++;
}
}
const a = fn();
다음과 같이 무한 반복문에서 제너레이터 함수를 이용하면 필요한 순간에만 계산을 하기 때문에
계산을 하기 직전까지 연산을 미뤄둘 수 있다는 장점이 있다.
function* gen1(){
yield "W";
yield "o";
yield "r";
yield "l";
yield "d";
}
function* gen2(){
yield "Hello,";
yield* gen1();
yield "!";
}
console.log(...gen2());
--> Hello, W o r l d !
제너레이터는 다른 작업을 하다가 다시 돌아와서 next() 해주면 진행이 멈췄던 부분 부터 이어서 실행합니다.
ex) Redux saga