ARTICLE | [JS] Promise, async, await

noopy·2021년 11월 22일
0

✏️ STUDY

목록 보기
8/10

팀원들과 함께하는 모던 JS 딥다이브 스터디 10차 💕

함수형 프로그래밍

이터러블

  • 이터러블 프로토콜을 준수한 객체
  • 이터레이터를 리턴하는 [Symbol.iterator]를 프로퍼티 키로 가진 객체

이터러블 확인법

이터러블은 이터레이터를 반환하는 [Symbol.iterator]를 프로퍼티 키로 갖고 있기 때문에 해당 키가 있는지 확인하면 이터러블인지 알 수 있다.

프로토타입 체인

이터레이터

  • 이터레이터 프로토콜을 준수한 객체
  • 이터러블의 [Symbol.iterator] 메서드를 호출할 때 반환되는 객체
  • next 메서드를 가지고 있으며 next 메서드 호출 시 (이터러블을 순회하며) value, done 프로퍼티를 갖는 이터레이터 리절트 객체를 리턴
  • 이터러블 요소 탐색을 위한 포인터 역할

이터레이션 프로토콜

  • 이터러블 프로토콜, 이터레이터 프로토콜을 준수하는 것들을 이터러블로 통일화하여 for of문, 스프레드 문법, 배열 구조분해 할당 등으로 사용할 수 있도록 약속한 규약

for of 문

for of문은 내부적으로 이터레이터의 next 메서드를 호출해 반환된 이터레이터 리절트 객체의 value 프로퍼티 값을 for of 문의 변수에 할당한다.

제너레이터

Promise, async, await

Promise 객체는 뭘까?

  • 완전 별도의 scope에 존재하는 친구다.
    async 함수는 무조건 promise를 반환한다. 그러면 Promise의 resolve된 값을 외부 스코프에서 받아올 방법이 없다. (외부 scope도 asnyc 함수가 아닌 이상)

callback 함수의 단점으로 외부의 값을 저장하기 어렵다. 근데 Promise와 async도 어렵다?

Promise의 경우엔, 내부에서 return을 하더라도 의미가 없다. 혹은 변수에 할당하는 형태로 사용할 수 없다.

우리가 JS를 import 해올 때 모듈로 불러와진다. 모듈은 기본적으로 strict 모드로 동작하기 때문에 전역 스코프는 asnyc가 될 수 없도록 되어있다.
결국 근본적으로는 asnyc, await 또한 갇혀있는 것이 아닌가. 전역에서 쓸 수 없는 것이 아닌가. 결국 결과값을 외부에 반환하는 것을 asnyc, await이 해결되었다고 하더라도 결국엔 asnyc 함수 내부에서만 쓸 수 있기 때문에 완전히 해결된 것은 아니라고 할 수 있다.
👉🏻비동기를 동기"처럼" 사용할 수 있다.
await의 장점은 비동기 호출을 실패했을 때! uncaught 에러가 생긴다는 점.

실행컨텍스트랑 연관시키면 쉽게 이해가된다.

에러는 호출자의 방향으로 전파가 된다. await를 쓰지 않은 비동기 함수의 경우 콜백함수가 실행되는 시점에 콜백함수가 들어가 있었던 함수의 콜스택은 이미 사라진 다음이다. 콜백함수는 어떻게 보면 자기가 정의되었던 함수가 아니라, 자기를 스케쥴링한 실행컨텍스트는 이미 사라지고 존재하지 않는 상태에서 실행이 되기 때문에 에러를 캐치할 호출자가 없다.

await 경우에는 호출되는 시점에 여전히 async 함수는 여전히 존재하는 상태이므로 에러가 호출자 방향으로 전파될 수 있다.

Response.json

Response 객체는 프로미스가 아닌 객체이지만, Response의 값은 Promise를 반환.
await로 const res 변수에 할당. > res에 할당된 값은 일반적인 값이 아니라 json이라는 메서드를 가지고 있는 Response 객체
Response 객체는 fetch를 호출했을 때의 (await를 통해) fullfilled된 객체.


json 형태가 아니라 text 형태여도 fetch API는 값을 담고 있는 response 객체를 리턴하기 때문에, 바로 반환된 값을 사용할 수는 없다. 따라서 위에 적혀있는 메서드로 한번 풀어줘야 된다.

json()은 프로미스를 반환하는데 왜 다음 then 내부의 console엔 promise가 안찍힐까?

then메서드는 프로미스일 경우 프로미스를 그냥 넘기고 아닐 경우 프로미스로 감싸서 넘긴다.

비동기와 Promise

  • 생성자의 처음에는 resolve, reject가 들어감. resolve는 바로 fullfilled되서 나오는 거고, 2번은 then으로 체이닝을 한 프로미스가 반환된 거기 때문에 여전히 pending 상태인 것이다.

  • 하나의 코드블록이라 했을 때, 프로미스 객체를 생성자 함수로 호출하면 첫번째는 동기적으로 진행이 되는 것.
    resolve를 받아서 resolve를 호출했기 때문에 동기적으로 이루어져서 바로 Promise에는 fullfilled된 프로미스가 담기는 것이다.
    두번째는 then 메서드 전까진 fullfilled된 상태이지만 then부분은 마이크로 테스트큐로 건너가서 비동기 처리를 하는 곳으로 건너가는 것이다. 실행 컨텍스트가 비기 전까지는 큐에 있는 것이고, 두번째 프로미스는 pending 상태에 있는 것이다.
    (then은 무조건 비동기. 동기적으로 처리할 수 없음. 왜냐면 then을 사용한 이후엔 callback이 마이크로테스트큐에 들어가니까)

resolve로 바로 fullfilled된 상태면 pending 단계가 존재하지 않는 것인가?

  • pending 단계가 없는 게 맞다.
    하지만 결국 resolve된 값을 사용하려면 then 메서드를 사용해야 하기 때문에 비동기 역할을 할 수 있다.
  • pending에서 fullfilled로 넘어가는 건 동기적으로 이루어진 게 아닌가?
    모순적이다. pending이라는 상태는 없을 것이다.
    new 키워드를 붙였으니까 Promise 인스턴스 객체가 생겼을 것이고, 인스턴스 내부에 status가 fullfilled된 상태일 것이라 추측된다.
  • p.851쪽. "생성된 직후의 프로미스는 기본적으로 pending 상태이다".
    내부적으로 pending이었다 fullfilled된 것이라 생각되기 때문에 모든 경우에 pending을 거친다 생각된다.
  • Promise는 생성과 동시에 status를 갖는 객체다. pending 상태가 조금이라도 있으면 동기처리가 된다는 게 말이 안된다 생각된다. pending에서 fullfilled로 동기적으로 넘어가는 게 이상하다.

문제 풀어보기

결론

  • 하나하나 then 메서드로 프로미스 내부 데이터를 가져오는 번거로운 작업을 asnyc, await으로 간편하게 진행되고 순서도 동기처리 될 수 있게 함.
profile
💪🏻 아는 걸 설명할 줄 아는 개발자 되기

0개의 댓글