일반 함수는? 하나의 값 만을 반환한다.
여러 개의 값을 필요에 따라 하나씩 반환
할 수 있다.function*
제너레이터 함수를 호출하면 코드가 실행되지 않고, 제너레이터 객체
가 반환된다.
function* generatorFunc() {
yield 1;
yield 2;
return 3;
}
let generator = generatorFunc();
console.log(generator); // Object [Generator] {}
yield <value>
문을 만날 때까지 실행이 지속yield <value>
문을 만나면 실행이 멈추고 산출하고자 하는 값인 value
가 바깥 코드에 반환yield <value>
문을 만날 때까지 실행이 지속function* generatorFunc() {
yield 1;
yield 2;
return 3;
}
let generator = generatorFunc();
let one = generator.next();
console.log(one); // { value: 1, done: false }
let two = generator.next();
console.log(two); // { value: 2, done: false }
let three = generator.next();
console.log(three); // { value: 3, done: true }
let four = generator.next();
console.log(four); // { value: undefined, done: true }
제너레이터는 iterable
for...of 반복문
을 사용해 값을 얻을 수 있음
return 값을 반환할 수 없음 return 3 → yield 3
제너레이터 객체를 spread 연산자
를 통해 iterable array
로 바꿀 수 있음
function* generatorFunc() {
yield 1;
yield 2;
return 3;
}
let generator = generatorFunc();
for (let value of generator) {
console.log(value);
}
// 1, 2
let sequence = [...generatorFunc()];
console.log(sequence); // [1, 2]
iterator 간단하게 알아보기
let obj = {
one: 1,
two: 2,
};
console.log(...obj);
// TypeError: Found non-callable @@iterator
객체는 iterable하지 않다. spread 연산자는 사용할 수 있다.
key: value 로 이루어진 쌍이므로, 출력은 불가능하다.
단, iterator 객체를 사용할 경우 next() 메서드를 사용해 간단한 범위 반복자를 형성할 수 있다.
// iterator
let range = {
from: 1,
to: 5,
[Symbol.iterator]() {
return {
current: this.from,
last: this.to,
next() {
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return { done: true };
}
},
};
},
};
console.log(...range); // 1, 2, 3, 4, 5
// generator
range = {
from: 1,
to: 5,
*[Symbol.iterator]() {
// [Symbol.iterator]: function*()를 짧게 줄임
for (let value = this.from; value <= this.to; value++) {
yield value;
}
},
};
제너레이터 안에 제너레이터를 임베딩할 수 있는 기능
function* generateSequence(start, end) {
for (let i = start; i <= end; i++) yield i;
}
function* generatePasswordCodes() {
// 0..9
yield* generateSequence(48, 57);
// A..Z
yield* generateSequence(65, 90);
// a..z
yield* generateSequence(97, 122);
// for (let i = 48; i <= 57; i++) yield i;
// for (let i = 65; i <= 90; i++) yield i;
// for (let i = 97; i <= 122; i++) yield i;
}
let str = "";
for (let code of generatePasswordCodes()) {
str += String.fromCharCode(code);
}
console.log(str); // 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
generator.next(arg)
: 값을 안, 밖으로 전달할 수 있다. arg는 yield의 결과
function* gen() {
let ask1 = yield "2 + 2 = ?";
console.log(ask1); // 4
let ask2 = yield "3 * 3 = ?";
console.log(ask2); // 9
}
let generator = gen();
console.log(generator.next().value); // **"2 + 2 = ?"**
console.log(generator.next(4).value); // "3 * 3 = ?"
console.log(generator.next(9).done); // true
첫 generator.next() 메서드
는 인자의 유무에 상관없이 첫 yield의 값을 받는다."2 + 2 = ?"
generator.next(4)
의 argument인 4는 바로 전 yield가 들어간 ask1의 위치에 들어간다. 그리고 두번째 yield의 값을 받은 객체를 받는다.ask1 = 4
{ value:"3 * 3 = ?", done: false }
{ value: undefined, done: true }
를 받는다.외부코드가 에러를 만들거나 던질 수 있다.
function* gen() {
try {
let result = yield "2 + 2 = ?"; // (1)
console.log(
"위에서 에러가 던져졌기 때문에 실행 흐름은 여기까지 다다르지 못합니다."
);
} catch (e) {
console.log(e); // 에러 출력
}
}
let generator = gen();
let question = generator.next().value;
console.log(question); // 2 + 2 = ?
generator.throw(new Error("데이터베이스에서 답을 찾지 못했습니다.")); // (2)
(2) 에서 제너레이터로 던진 에러는 yield와 함께 (1)로 던져진다.
던져진 에러는 catch로 전달된다.
제너레이터 바깥에서 error를 잡을 수도 있다.
function* generate() {
let result = yield "2 + 2 = ?"; // Error in this line
}
let generator = generate();
let question = generator.next().value;
try {
generator.throw(new Error("데이터베이스에서 답을 찾지 못했습니다."));
} catch(e) {
alert(e); // 에러 출력