모던 자바스크립트 Deep Dive 46장 정리 - 제너레이터와 async/await

Hyodduru ·2022년 11월 19일
0
post-thumbnail

제너레이터란?

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 메서드까지도 갖는다.

✔️ 메서드 동작 방식

  • next 메서드 호출 시 제너레이터 함수의 yield 표현식까지 코드 블록을 실행하고 yield된 값을 value 프로퍼티 값으로, false를 done 프로퍼티 값으로 갖는 이터레이터 리절트 객체 반환
  • return 메서드 호출 시 인수로 전달받은 값을 value 프로퍼티 값으로, true를 done 프로퍼티 값으로 갖는 이터레이터 리절트 객체 반환
console.log(generator.next()); // {value : 1, done : false}
console.log(generator.return('End!')); // {value : 'End!', done : false}
  • throw 메서드 호출 시 전달받은 에러 발생시키고 undefined를 value 프로퍼티 값으로, true를 done 프로퍼티 값으로 갖는 이터레이터 리절트 객체 반환
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} 

제너레이터의 활용

이터러블의 구현

비동기 처리

async / await

제너레이터를 사용해서 비동기 처리를 동기처럼 동작하도록 구현하는 것은 코드가 장황해지고 가독성이 나빠짐.

ES8(ECMAScript 2017)에서는 제너레이터보다 간단하고 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현할 수 있는 async/await가 도입됨

async/await는 프로미스를 기반으로 동작. 프로미스의 then/catch/finally 후속 처리 메서드에 콜백함수를 전달해서 비동기 처리 결과를 후속 처리할 필요 없이 동기 처리처럼 프로미스를 사용할 수 있음.

async 함수

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 키워드

await 키워드는 프로미스가 settled 상태가 되면 프로미스가 resolve한 처리 결과를 반환. await 키워드는 반드시 프로미스 앞에 사용해야 함.

에러 처리

async/await 에서 에러처리는 try...catch 문 사용 가능.
프로미스를 반환하는 비동기 함수는 명시적으로 호출할 수 있기 때문에 호출자가 명확.

profile
꾸준히 성장하기🦋 https://hyodduru.tistory.com/ 로 블로그 옮겼습니다

0개의 댓글