만족
[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을 사용하거나 그대로 콜백 파티를 열어주면 된다.