만족

[Javascript] async-await와 Promise 본문

[Javascript] async-await와 Promise

Nodejs Satisfaction 2020. 1. 19. 02:39

두 개념 모두 비동기적인 작업을 좀더 직관적이고 편리하게 진행하기 위해 탄생한 개념이다.

 

아주아주 빠르게 실전압축으로다가 알아보자.

 

Promise는 무엇인가?

 

es6에서 처음 등장한 개념이다.

 

Promise는 pending(작동 중), fulfilled(성공적으로 완료), reject(실패)의 세 상태를 가진다.

 

미리 정의된 상태를 통해 비동기적인 작업을 쉽게 다룰 수 있다.

new Promise((resolve, reject)=>{
  //...
});

와 같이 선언한다.

 

(resolve, reject)=>{ ... } 에는 프로미스로 진행할 작업 내용을 정의하고,

성공 시 resolve( [전달하고싶은 값] ); 을 호출하고, reject( [전달하고 싶은 값;주로 에러 정보] )를 호출한다.

 

resolve가 실행되었을 때는 promise의 상태가 fulfilled 상태가 되고

reject가 실행되었을 때는 promise의 상태가 reject상태가 되고

아무것도 실행되지 않았을 때는 pending 상태가 된다.

 

그러면, 이제 이 결과값에 대한 처리를 어떻게 해 줄 것인지를 정의해주어야 한다.

 

new Promise((resolve, reject)=>{
  ...
  })
  //resolve(...)가 호출되었을 때 then으로 이동
  .then(param=>{ ... })
  //reject(...)가 호출되거나 exception이 발생했을 때 catch로 이동
  .catch(error=> { ...});

 

then은 promise에 전달한 함수에서 resolve(...)가 호출되었을 때(fulfilled상태일 때) 전달받은 함수를 실행한다.

 

가령

new Promise((resolve, reject)=>{resolve("success");}).then(param=>{ console.log(param); });

을 하면 resolve("success")가 호출되었으므로 콘솔에는 resolve에 전달된 값인 success가 찍히게 된다.

 

catch는 reject()가 호출되거나(reject상태일 때) then의 내용에서 예외가 발생할 경우 catch에 전달된 함수가 실행된다.

 

가령

new Promise((resolve, reject)=>{reject("fail");}).then(param=>{ ... }).catch(error=> console.log(error));

을 하면 reject("fail")가 호출되었으므로 콘솔에는 reject에 전달된 값인 fail이 찍히게 된다.

 

그런데, Promise는 비동기적으로 작동하므로, 실행 제어를 위해서는 코드가 매우 복잡해질 수 밖에 없다.

 

예를 들어 3개의 promise에 대해서 동시에 실행이 아닌 차례로 실행시키려고 한다면

각각의 promise의 then부분에 콜백으로 전달해서 처리해주어야 하는데

이는 지루하고 의미없는 작업이며, 코드의 가독성을 매우 떨어뜨린다.

 

new Promise((rs, rj)=> rs())
  .then(()=>{
    new Promise((rs, rj)=>rs())
      .then(()=>{
        //...
    //...
//...

 

이런 문제를 '콜백 지옥(callback hell)'이라고 한다.

에네르기파!!!

 

async-await

es7에서 등장한 개념이다.

 

promise로 비동기 작업에 대한 '정의'는 간편해졌지만, 흐름 제어는 여전히 '콜백'을 이용해야 했기에 매우 복잡했다.

그러나 async-await의 등장으로 콜백 지옥에서도 벗어날 수 있었다.

 

async는 함수 선언 앞에 붙여서, 이 함수가 '비동기 함수'임을 알려준다.

 

즉 

const asyncFunc= async ()=>{....}

const syncFunc= ()=>{

  asyncFunc();

  //SomeCodeBlock

}

syncFunc();

를 하면, asyncFunc()는 SomeCodeBlock과 플로우가 독립적으로 진행된다.

(즉, asyncFunc()가 완전히 종료되기 전에 SomeCodeBlock으로 넘어갈 수 있다)

(동기 함수의 경우 한쪽 방향으로 흐른다. 예를 들어 n번째 라인은 n-1번째 라인이 완료된 후에 실행된다)

 

await는 async함수 내의 비동기 프로시저 앞에 붙어서, 해당 비동기 프로시저가 끝날 때 까지 기다리게 한다.

 

예를 들어서

const syncFunc= ()=>{

  const p1= new Promise(...);

  const p2= new Promise(...);

  const p3= new Promise(...);

};

syncFunc();

 

에서는 p1,p2,p3중 어떤 순서로 작업이 완료될 지 알 수 없으며, 제어할 수도 없다.

 

그런데, 

const asyncFunc= async ()=>{

  await new Promise(...);

  await new Promise(...);

  await new Promise(...);

}

와 같이 쓰면, 첫 번째 promise가 끝날 때 까지 다음 라인으로 넘어가지 않고 기다리다가 완료되면 다음 promise를 실행하는 방식으로 실행한다.

즉 await 뒤에 붙은 promise에 대해 기다린다(wait)는 의미이다.

 

따라서 위에서 말한 콜백 지옥에 빠지지 않고, async-await를 이용하여 비동기 작업을 처리할 수 있다.

 

주의할 점은 Promise와 async-await는 모두 es5버전에서는 사용할 수 없는 기능이다.

즉, 인터넷 익스플로러에서 해당 코드를 굴리면 syntax error를 뱉고 뻗어버린다.

해당 환경을 반드시 고려해야 한다면, babel을 사용하거나 그대로 콜백 파티를 열어주면 된다.



Comments