자바스크립트는 함수가 실행되면 완료될 때까지 계속 실행되며 도중에 다른 코드가 끼어들지 못한다.
이를 완전-실행(Run-to-Completion)이라고 부른다.
ES6에 나온 제너레이터는 완전-실행 법칙을 따르지 않는다.
var x = 1;
function* foo() {
x++;
yield; // 멈추시오!
console.log("x:", x);
}
function bar() {
x++;
}
var it = foo();
// foo의 시작점!
it.next();
console.log(x); // 2
bar();
console.log(x); // 3
it.next(); // x: 3
foo()
가 실행되어도 완전-실행되지 않고 yield문에서 멈췄다. (실제로는 실행중이지만 멈춘 상태다.)
첫번째의 it.next()는 x++을 해주고 yield문에서 멈췄다.
두번째의 it.next()는 yield 이후의 console.log("x:", x);
를 실행한다.
제너레이터는 특별한 함수다. 그렇지만 함수의 기본 역할인 입력과 출력은 수행한다.
function* foo(x, y) {
return x * y;
}
var it = foo(6, 7);
var res = it.next();
console.log(res.value); // 42
next()의 결괏값은 *foo()가 반환한 값을 value 프로퍼티에 저장한 객체다.
즉, yield
는 실행 도중 제너레이터한테 값을 가져와, 반환시켜주는 역할이다.
function* foo(x) {
var y = x * (yield);
return y;
}
var it = foo(6);
var r = it.next(2);
console.log(r.value); // undefined
var r2 = it.next(5);
console.log(r2.value); // 30
var r3 = it.next(3);
console.log(r3.value); // undefined
어떻게 동작하는지 살펴보자.
var y = x * (yield)
까지만 실행시킨다. 그러고 yield를 리턴한다.x * 5
라는 표현식을 완성시킨다.r2.value
는 NaN
이 출력된다.같은 제너레이터한테 여러 이터레이터를 만들고, 그 이터레이터끼리 상호작용이 가능하다.
var z = 1;
function* foo() {
var x = yield 2;
z++;
var y = yield x * z;
console.log(x, y, z);
}
var it1 = foo();
var it2 = foo();
var val1 = it1.next().value; // 2
var val2 = it2.next().value; // 2
val1 = it1.next(val2 * 10).value; // 40, x:20, z:2
val2 = it2.next(val1 * 5).value; // 600, x: 200, z:3
it1.next(val2 / 2); // 300, x: 20, y: 300, z: 3
it2.next(val1 / 4); // 10, x: 200, y: 10, z: 3
위쪽의 예시에서는 next()만을 사용했다.
사실 이터레이터가 사용할 수 있는 메소드는 return()과 throw()도 있다.
function* foo() {
yield 1;
yield 2;
yield 3;
}
const it = foo();
var r1 = it.next();
console.log(r1); // { value: 1, done: false }
var r2 = it.return("asdf");
console.log(r2); // { value: "asdf", done: true }
var r3 = it.next();
console.log(r3); // { value: undefined, done: true }
return()
은 제너레이터를 완료 상태로 만들 수 있다.
function* foo() {
while (true) {
try {
yield 1;
} catch (e) {
console.log("에러입니닷!");
}
}
}
const it = foo();
var r1 = it.next();
console.log(r1); // { value: 1, done: false }
var r2 = it.throw(new Error("asdf"));
console.log(r2);
// "Error caught!"
// { value: 1, done: false }
throw()
는 에러를 던질 수 있게 해준다.
Refer - YDKJS this와 객체 프로토타입, 비동기와 성능