ES6에 도입된 제너레이터(generator)는 코드 불록의 실행을 일시 중지했다가 필요한 시점에 재개할 수 있는 특수한 함수
1. 제너레이터 함수는 함수 호출자에게 함수 실행의 제어권을 양도할 수 있다.
제너레이터 함수는 함수 호출자가 함수 실행을 일시 중지 시키거나 재개시키도록 할 수 있다. 일반 함수를 호출하는 경우, 함수 호출자는 함수를 호출한 이후 함수 실행을 제어할 수 없음.
2. 제너레이터 함수는 함수 호출자와 함수의 상태를 주고받을 수 있다.
제너레이터 함수는 함수 호출자에게 상태를 전달할 수 있고 함수 호출자로부터 상태를 전달받을 수도 있다. 일반 함수를 호출하는 경우, 함수가 실행되고 있는 동안 함수 외부에서 함수 내부로 값을 전달하여 함수의 상태를 변경할 수 없음.
3. 제너레이터 함수를 호출하면 제너레이터 객체를 반환한다.
제너레이터 함수를 호출하면 함수 코드를 실행하는 것이 아니라 이터러블이면서 동시에 이터레이터인 제너레이터 객체를 반환함.
제너레이터 함수는 function*
키워드로 선언함. 그리고 하나 이상의 yield 표현식을 포함함.
// 제너레이터 함수 선언문
function* genDecFunc() {
yield 1;
}
// 제너레이터 함수 표현식
const genDecFunc = function* () {
yield 1;
};
// 제너레이터 메서드
const obj = {
* genObjMethod() {
yield 1;
}
};
// 제너레이터 클래스 메서드
class MyClass {
* genObjMethod() [
yield 1;
}
}
🔖 에더리스크(*
) 위치는 function 키워드와 함수 이름 사이라면 어디든지 상관없다. 하지만 일관성을 유지하기 위해 function 키워드 바로 뒤에 붙이는 것을 권장.
✔️제너레이터 함수는 화살표 함수로 정의할 수 없음.
✔️ 제너레이터 함수는 new 연산자와 함께 생성자 함수로 호출할 수 없음.
제너레이터 함수를 호출하면 일반 함수처럼 함수 코드 블록을 실행하는 것이 아니라 제너레이터 객체를 생성해 반환함. 제너레이터 함수가 반환한 제너레이터 객체는 이터러블(iterable)이면서 동시에 이터레이터(iterator)
// 제너레이터 함수
function* getFunc() {
yield 1;
yield 2;
}
// 제너레이터 함수를 호출하면 제너레이터 객체를 반환함.
const generator = getFunc();
// 이터러블은 Symbol.iterator 메서드를 직접 구현하거나 프로토타입 체인을 통해 상속받은 객체다.
console.log(Symbol.iterato in generator); // true
// 이터레이터는 next 메서드를 갖는다.
console.log('next' in generator); // true
✔️ 제터레이터 객체는 이터레이터에는 없는 return, thorw 메서드까지도 갖는다.
✔️ 메서드 동작 방식
console.log(generator.next()); // {value : 1, done : false}
console.log(generator.return('End!')); // {value : 'End!', done : false}
consol.log(generator.throw('Error!')); // value : undefined, done : true}
제너레이터는 yield 키워드와 next 메서드를 통해 실행을 일시 중지했다가 필요한 시점에 다시 재개할 수 있음.
yield 키워드는 제너레이터 함수의 실행을 일시 중지시키거나 yield 키워드 뒤에 오는 표현식의 평가 결과를 제너레이터 함수 호출자에게 반환함.
console.log(generator.next()); // {value : 1, done : false}
console.log(generator.next()); // {value : 2, done : true}
제너레이터 객체의 next 메서드를 호출하면 yield 표현식까지 실행되고 일시 중지됨. 이때 함수의 제어권이 호출자로 양도(yield)된다.
제너레이터 객체의 next 메서드에 전달한 인수는 제너레이터 함수의 yield 표현식을 할당받는 변수에 할당됨.
function* genFunc(){
const x = yield + ;
const y = yield (x + 10);
return x + y;
}
const generator = genFunc(0);
let res = generator.next();
console.log(res); // {value : 0, done : false}
res.genrator.next(10);
console.log(res); // {value : 20, done : false}
res = generator.next(20);
// yield가 두번있었으므로 세번째 next 호출은 done이 true
console.log(res); // {value : 30, done : true}
제너레이터를 사용해서 비동기 처리를 동기처럼 동작하도록 구현하는 것은 코드가 장황해지고 가독성이 나빠짐.
ES8(ECMAScript 2017)에서는 제너레이터보다 간단하고 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현할 수 있는 async/await가 도입됨
async/await는 프로미스를 기반으로 동작. 프로미스의 then/catch/finally 후속 처리 메서드에 콜백함수를 전달해서 비동기 처리 결과를 후속 처리할 필요 없이 동기 처리처럼 프로미스를 사용할 수 있음.
await 키워드는 반드시 async 함수 내부에서 사용해야 함.
async 함수는 언제나 프로미스를 반환. async 함수가 명시적으로 프로미스를 반환하지않더라도 async 함수는 암묵적으로 반환값을 resolve하는 프로미스를 반환
const bar = async function(n) { return n; };
bar(2).then(v => console.log(v)); // 2
🔖 클래스의 constructor 메서드 async 가 될 수 없음. 클래스의 constructor 메서드는 인스턴트를 반환해야 하지만 async 함수는 언제나 프로미스를 반환해야 함.
clsss MyClass {
async constructor() {}
// SynctaxError
}
await 키워드는 프로미스가 settled 상태가 되면 프로미스가 resolve한 처리 결과를 반환. await 키워드는 반드시 프로미스 앞에 사용해야 함.
async/await 에서 에러처리는 try...catch 문 사용 가능.
프로미스를 반환하는 비동기 함수는 명시적으로 호출할 수 있기 때문에 호출자가 명확.