46장 제너레이터와 async/await
46.1 제너레이터란?
일반 함수
- 함수 실행 제어권 : 함수 호출 시 제어권이 함수에게 넘어가,(콜스택에 함수 실행 컨텍스트 생성) 코드를 일괄 실행함. 함수 호출자는 함수 실행을 제어할 수 없음.
- 반환값 : 함수 호출 시 매개변수를 통해 외부 값을 받고, 코드 일괄 실행 후 결과값을 외부로 반환함.
- 함수 상태 변경 : 함수가 실행되는 동안 외부에서 함수의 상태를 변경할 수 없음.
제너레이터 함수
- 함수 실행 제어권 : 함수 호출자가 함수 실행을 중지, 재개시키는 등 제어할 수 있음.
- 반환값 : 제너레이터 함수 호출 시, 함수 코드를 실행하는 게 아니라 제너레이터 객체(이터러블이자 이터레이터)를 반환함.
- 함수 상태 변경 : 함수 호출자와 양방향으로 함수 상태를 서로 주고받을 수 있음.
※ 제너레이터는 제너레이터 함수를 호출한 호출자이자, 반환된 제너레이터 객체를 말하는 것 같음.
예제들을 보면, 어떤 변수에 제너레이터 함수를 호출하여 반환된 제너레이터 객체를 할당함. 그리고 제너레이터 객체가 할당된 변수를 제너레이터라고 부르는 것 같음.
46.2 제너레이터 함수의 정의
문법
- function* 키워드
- *(애스터리스크) : function 키워드와 함수 이름 사이에 위치
- 함수 몸체에 하나 이상의 yield(양도) 표현식
- 메서드도 제너레이터로 정의 가능
// 객체 안의 제너레이터 메서드
const obj = {
* genObjMethod() {
yield 1;
}
};
// 제너레이터 클래스 메서드
class MyClass = {
* genClsMethod() {
yield 1;
}
};
주의 사항
- 화살표 함수로 정의 불가
- new 연산자와 함께 생성자 함수로 호출 불가
46.3 제너레이터 객체
제너레이터 함수를 호출하면 코드 블록을 실행하는 게 아니라, 제너레이터 객체를 생성하여 반환함.
제너레이터 객체의 특징
- 이터러블 : Symbol.iterator 메서드 상속받음.
- 이터레이터 : next 메서드(value, done 프로퍼티 갖는 result 객체 반환) 소유.
- 호출 시, 제너레이터 함수의 yield 표현식까지 실행하여 { value : yield된 값, done : false }를 프로퍼티로 갖는 리절트 객체 반환.
- 남은 yield 표현식이 없으면 { value : undefined, done : true }
- return 메서드 : 호출 시, { value : 인수, done : true } 리절트 객체 반환
- throw 메서드 : 호출 시, 인수로 받은 에러를 발생시킨 후, { value : undefined, done : true } 리절트 객체 반환
46.4 제너레이터의 일시 중지와 재개
제너레이터는 yield 키워드와 next 메서드를 통해 실행을 일시 중지하고, 재개함. 즉, 함수 호출자가 제어권을 양도 받아 필요할 때 함수 실행을 재개함.
제너레이터 동작 과정
[ 874쪽(908) 예제 46-8, 876쪽(910) 예제 46-9 ]
- 제너레이터 함수를 호출하여, 반환된 제너레이터 객체의 next 메서드를 호출하면, 제너레이터 함수의 코드 블록에서 yield 표현식까지 실행함.
- 따라서 yield 키워드는 제너레이터 함수의 실행을 일시 중지시키는 역할. yield를 기준으로 함수 실행을 중지시킴으로써, 함수 호출자가 함수의 제어권을 갖게 됨(양도).
- 동시에, next 메서드가 반환하는 이터레이터 리절트 객체의 value 프로퍼티 값으로서 yield 키워드 뒤의 표현식의 평가 결과가 제너레이터 함수 호출자에게 반환됨.
- next 메서드에 인수를 전달하면 yield 표현식을 할당 받는 변수에 대신 할당됨.
- 처음 호출하는 next 메서드에는 인수를 전달하지 않는다. 전달되면 무시됨.
- yield 표현식을 할당 받는 변수 : yield 표현식이 평가되어 value 프로퍼티에 할당된 다음 next 메서드가 호출될 때 변수에 할당됨. 즉, 제너레이터 함수 안에서 첫번째 yield 표현식을 할당 받는 변수는 두 번째 next 메서드가 호출될 때 값을 할당 받음. 이때 인수를 전달받으면 첫번째 yield 표현식이 아니라 인수 값을 할당 받음.
- 남은 yield 표현식이 없으면 함수를 끝까지 실행하여 제너레이터 함수의 반환값(return)을 value 프로퍼티에 할당하고, done 프로퍼티의 값은 true가 됨.
- 제너레이터 함수의 반환값은 의미가 없음. return은 종료의 의미로 사용.
이해 안 되는 부분
// 예제 46-9
function* genFunc() {
const x = yield 1;
const y = yield(x+10);
return x + y;
}
const generator = genFunc(0); // 매개변수가 없는데 인수로 전달한 0이 뭔지 궁금함
제너레이터 함수의 양방향 상태 변경
제너레이터 함수의 경우, 함수 호출자가 next 메서드를 통해 yield 표현식까지의 코드를 실행시켜 제너레이터 객체가 관리하는 상태(yield된 값, yield 키워드 뒤에 오는 표현식)을 리절트 객체의 value 프로퍼티로 꺼내올 수 있음.
또한 next 메서드에 인수를 전달하면 yield 표현식을 할당받는 변수에 상태를 밀어넣을 수 있음.
46.5 제너레이터의 활용
- 이터러블 구현 : 제너레이터 함수를 사용하면 이터레이션 프로토콜을 준수하여 이터러블 생성하는 방식보다 쉬움.
- 예) 피보나치 수열
- 비동기 처리 : 함수 호출자와 함수의 상태를 주고받는 특성을 활용하여 프로미스를 사용한 비동기 처리를 then/catch/finally 같은 후속 처리 메서드 없이 동기 처리할 수 있음.
- 예) 제너레이트 실행기 => 사실 async/await 쓰면 필요 없음.
46.6 async/await
제너레이터를 사용한 비동기의 동기 처리가 복잡해서 ES8에서 도입됨.
async/await는 프로미스 기반으로, then/catch/finally 메서드에 콜백 함수를 전달하여 비동기 처리 결과를 후속 처리할 필요 없이 동기 처리처럼 프로미스를 사용할 수 있게 해줌.
[ 880쪽(914) 예제 46-14 ]
46.6.1 async 함수
- await 키워드는 async 함수 안에서만 사용 가능.
- async 키워드는 function 키워드 혹은 (매개변수) 앞에 씀. 메서드일 땐 함수 이름 앞에 씀.
- async 함수는 암묵적으로 반환값을 resolve하는 프로미스를 반환함.
- 따라서 클래스의 constructor 메서드는 항상 인스턴스를 반환해야 하므로 async 함수(메서드)가 될 수 없음.
46.6.2 await 키워드
- await 키워드는 반드시 프로미스 앞에 사용.
- 프로미스가 settled 상태(비동기 처리가 수행된 상태)가 되길 기다리다가 resolve 처리 결과를 반환함.
- Promise.all([ 프로미스1, 프로미스2, 프로미스3, ... ]) : 연관되어있지 않고 개별적으로 수행되는 비동기 처리가 여러 개 있을 때는 순차적으로 처리하지 말고, 이렇게 묶어서 처리하는 게 좋음.
46.6.3 에러 처리
- 에러는 호출자 방향, 즉 콜스택의 아래 방향으로 전파됨. 하지만 비동기 함수의 콜백 함수를 호출한 것은 비동기 함수가 아니기 때문에 try catch 문으로 에러 캐치 불가.
- async/await를 사용하면 프로미스를 반환하는 비동기 함수의 호출자가 명확하기 때문에 try catch로 에러 캐치 가능.
- async 함수는 try catch로 에러 처리를 하지 않았을 때 발생한 에러를 reject 하는 프로미스를 반환하므로, async 함수 호출 후 catch 메서드로 후속 처리하는 방법도 있음. [ 885쪽(919) 예제 46-23 ]
'TIL' 카테고리의 다른 글
Axios (0) | 2022.08.09 |
---|---|
Recoil (0) | 2022.08.09 |
[TIL-188] MyView 프로젝트 - MUI X DateTimePicker 사용하기 (0) | 2022.06.15 |
[TIL-187] MyView 프로젝트 - React props로 컴포넌트 전달 & text-transform (0) | 2022.06.05 |
[TIL-186] 모던 자바스크립트 - 32~33, 38장 브라우저의 렌더링 과정 (0) | 2022.06.03 |