이전 포스팅에서 봤던 이너레이터가 값을 읽어오기 위한 인터페이스라면 제너레이터는 값을 쓰기 위한 인터페이스이다. 제너레이터는 또한 interator protocol 규약을 준수해서 반복문인 for..of문과 Spread문법에서 사용할 수 있다.
제너레이터 함수
제너레이터는 일반적인 함수와는 다르게 * 함수뒤나 함수명 앞에 * 를 붙입니다.
1
2
3
4
5
function* test() {
return 1;
}
const gen = test();
console.log(gen);//test {<suspended>}
test 함수를 호출했는데 왜 값이 3 반환이 안될까??
제너레이터 함수는 일반적인 함수와 다르게 함수를 호출하게 되면 함수가 실행이 되지않고 실행을 처리하는 제너레이터 객체가 반환이 됩니다.
이 안에 객체를 사용하기 위해선 next란 메서드와 yield란 객체가 필요합니다.
yield,next
yield
와 next
는 제너레이터에서의 중요한 메서드입니다.
next
메서드는 generate함수를 실행시키는 메서드라 생각하시면 됩니다.
next
메서드를 호출하게 되면 yield<value>
문을 만날때까지 실행이 계속됩니다.
yield<value>
문을 만나게 되면 함수의 실행이 멈추고 value 값이 반환이 됩니다.
yield
는 제너레이터 함수가 실행될때 debuging모드에서의 break point처럼 중간에 함수를 정지를 시키며 yield
뒤에 오는 표현식이 반환됩니다.
1
2
3
4
5
6
7
8
9
10
11
function* test() {
yield 1;
yield 2;
return 3;
}
const gen = test();
console.log(gen.next());//{value: 1, done: false}
console.log(gen.next());//{value: 2, done: false}
console.log(gen.next());//{value: 3, done: true}
console.log(gen.next());//{value: undefined, done: true}
위 예제에서도 gen함수의 next
메서드를 호출하였을 때 순차적으로 yield
에 멈추게 되고 yield
의 value값이 반환이 됩니다.
마지막으로는 return문에 다다르고 제너레이터가 종료가 됩니다.
제너레이터가 종료된 후 next메서드를 호출하여도 value 값은 undefinded입니다.
제너레이터의 이터러블 구현
1
2
3
4
5
6
7
8
9
10
11
12
13
function* test() {
yield 1;
yield 2;
return 3;
}
let gen = test();
for(let value of gen) {
console.log(value); // 1, 2가 출력됨
}
제너레이터도 이터러블처럼 fot..of 반복문을 사용해서 값을 얻을 수 있습니다.
하지만 위예제문에서의 return 값은 반환이 안되죠 그 이유는
이터러블의 {value,done}프러퍼티에서 done의 값이 true일 경우 value값은 무시가 되기 때문입니다.
그러므로 반복문에서 제너레이터를 사용할 경우 모든 값은 yield
로 반환해주어야 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const test = (function* () {
let cur = 0;
while (true) {
cur+=1
yield cur;
}
}());
// infiniteFibonacci는 무한 이터러블이다.
for (const num of test) {
if (num > 10) break;
console.log(num);
}
또한 Spread문법을 이용하여 작성할수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
const test = function* (max) {
let cur = 0;
while (true) {
cur+=1
if (cur >= max) return; // 제너레이터 함수 종료
yield cur;
}
};
console.log([...test(10)]);
예외처리
제너레이터의 인자값은 yield
의 value에 값이 지정됩니다.
하지만 next
이외에도 generate에 throw란 메서드를 이용해서 값을 던질 수 있습니다.
throw
의 경우에는 next
메서드와 달리 yield
의 값에 지정이 되지 않고 generate의 오류 메세지에 바인딩이 됩니다.
1
2
3
4
5
6
7
8
9
10
function* test() {
let result = yield 1; // Error in this line
}
let gen = test();
let value = gen.next();
gen.throw(new Error("에러 발생!!"));
비동기처리
또한 제너레이터는 Promise
처럼 비동기 처리를 동기 방식으로 구현할 수 있다.
generate함수안에 yield
값에 반환되는 비동기 함수를 넣어주면 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function getUser(genObj, username) {
fetch(`https://api.github.com/users/${username}`)
.then(res => res.json())
.then(user => genObj.next(user.name));
}
const gen = (function* () {
let user;
user = yield getUser(g, 'kakao');
console.log(user); // kakao
user = yield getUser(g, 'naver');
console.log(user); // Naver
}());
gen.next();
참고
https://poiemaweb.com/es6-generator https://ko.javascript.info/generators#ref-1932