[JS]46장 async/await

JH Cho·2022년 12월 22일
0

모던JS DeepDive 공부

목록 보기
21/27
post-thumbnail

46.1_ 제너레이터

  • es6의 제너레이트넌 코드 블록의 실행을 일시 중지했다가 필요한 시점에 재개할 수 있는 특수 함수.

나중에 정리

46.6_ async / await

  • 제너레이터를 사용하여 비동기 처리를 동기 처리처럼 동작하도록 구현하면 코드가 길어지고 가독성이 나빠진다.
  • ES8에서 더 간단하고 가독성 좋게 비동기 처리를 동기 처리처럼 동작하도록 구현하는 async / await가 도입
  • async / await는 프로미스 기반 동작
  • then/catch/finally 처럼 비동기 처리 결과를 후속처리할 필요가 없음.
    • 그냥 동기처럼 프로미스가 처리 결과를 반환하도록 구현 가능.

46.1_ async 함수

  • await 키워드는 반드시 async 함수 내부에서 사용
  • async 함수는 언제나 프로미스를 반환
  • 명시적으로 프로미스를 반환하지 않아도 async 함수는 암묵적으로 반환값을 resolve하는 프로미스를 반환
  • 기존 프로미스 생성 함수보다 간단하게 promise를 생성함
function fetchUser() {
  return new Promise((resolve, reject)=>{
    resolve('hi');
  });
}
// async 함수
async function fetchUser() {
  return 'hi';
}

const user = fetchUser();
user.then(console.log);
console.log(user)

[[Prototype]]
:
Promise
[[PromiseState]]
:
"fulfilled"
[[PromiseResult]]
:
"hi"



>- async 사용
```js
// async 함수 선언문
async function foo(n) { return n; }
foo(1).then(v => console.log(v)); // 1
>
// async 함수 표현식
const bar = async function (n) { return n; };
bar(2).then(v => console.log(v)); // 2
>
// async 화살표 함수
const baz = async n => n;
baz(3).then(v => console.log(v)); // 3
>
// async 메서드
const obj = {
  async foo(n) { return n; }
};
obj.foo(4).then(v => console.log(v)); // 4
>
// async 클래스 메서드
class MyClass {
  async bar(n) { return n; }
}
const myClass = new MyClass();
myClass.bar(5).then(v => console.log(v)); // 5

다만 클래스의 컨스트럭터 메서드는 async 메서드가 될 수 없음.
컨스트럭터는 인스턴스를 반환하기 때문.

46.6.2_ await 키워드

  • await는 프로미스가 settled 상태(비동기 처리가 수행된 상태)가 될 때까지 대기하다가 settled 되면 프로미스가 resolve한 처리 결과를 반환
  • await는 반드시 프로미스 앞에 사용해야 함.
const fetch = require('node-fetch');

const getGithubUserName = async id => {
  const res = await fetch(`https://api.github.com/users/${id}`); // ①
  const { name } = await res.json(); // ② 
  console.log(name); // Ungmo Lee
};

getGithubUserName('ungmo2');
  1. fetch함수가 서버의 응답을 받아 반환한 프로미스가 settled 상태가 될 때까지 대기
  2. 이후 settled가 resolve한 처리 결과가 res에 할당
  • 쓸 데 없이 남발 주의
async function foo() {
  const a = await new Promise(resolve => setTimeout(() => resolve(1), 3000));
  const b = await new Promise(resolve => setTimeout(() => resolve(2), 2000));
  const c = await new Promise(resolve => setTimeout(() => resolve(3), 1000));

  console.log([a, b, c]); // [1, 2, 3]
}

foo(); // 약 6초 소요된다.
  • 위의 세 프로미스는 서로 연관이 없이 개별적으로 수행되는 비동기 처리이다.
  • 따라서 순차적 처리가 필요없으므로 위와 같이 사용은 지양하자.

😽 올바른 사용

async function foo() {
  const res = await Promise.all([
    new Promise(resolve => setTimeout(() => resolve(1), 3000)),
    new Promise(resolve => setTimeout(() => resolve(2), 2000)),
    new Promise(resolve => setTimeout(() => resolve(3), 1000))
  ]);

  console.log(res); // [1, 2, 3]
}

foo(); // 약 3초 소요된다.

46.6.3_ 에러처리

  • 콜백 패턴의 가장 큰 단점은 에러 처리가 곤란함이다.
  • 45장. 에러는 호출자 방향으로 전파됨.( 콜 스택의 아래 방향.. 실행중인 컨텍스트 직전에 푸쉬된 실행 컨텍스트 방향으로)
  • 하지만 비동기 함수의 콜백함수를 호출한 것은 비동기 함수가 아니라서 try...catch문을 사용해 에러 캐치 불가
try {
  setTimeout(() => { throw new Error('Error!'); }, 1000);
} catch (e) {
  // 에러를 캐치하지 못한다
  console.error('캐치한 에러', e);
}
  • async / await에서 try...catch 사용 가능
  • 프로미스를 반환하는 비동기 함수는 명시적 호출이 가능하여 호출자가 명확함.
const fetch = require('node-fetch');

const foo = async () => {
  try {
    const wrongUrl = 'https://wrong.url';

    const response = await fetch(wrongUrl);
    const data = await response.json();
    console.log(data);
  } catch (err) {
    console.error(err); // TypeError: Failed to fetch
  }
};

foo();
  • async함수 내의 catch는 http 통신 에러 뿐만 아니라 try 문 내의 일반적 에러까지 캐치 가능.
  • async 함수 내에서 catch 문을 사용해서 에러 처리를 안하면 async 함수는 발생한 에러를 reject 하는 프로미스를 반환한다.
  • async 함수 호출 후 발생한 에러를 캐치
const fetch = require('node-fetch');

const foo = async () => {
  const wrongUrl = 'https://wrong.url';

const response = await fetch(wrongUrl);
const data = await response.json();
return data;
};

foo()
.then(console.log)
.catch(console.error); // TypeError: Failed to fetch

profile
주먹구구식은 버리고 Why & How를 고민하며 프로그래밍 하는 개발자가 되자!

0개의 댓글