본문 바로가기

플밍 is 뭔들/JavaScript&jQuery

[JavaScript] ECMAScript 2015 / ES6 (4) / Promise, async-await

※ 프로미스(Promise)

싱글쓰레드인 자바스크립트에서 비동기 처리를 위해서 콜백(callback)을 사용해 왔다.
그 결과 비동기 처리를 온전히 해낼 수 있었지만 이런 콜백이 사용되는 경우가 많아지면서 단점이 드러났다.
단점은 비동기 처리를 순차적으로 해야할 경우 비동기 처리를 중첩시켜서 표현하므로 에러, 예외처리가 어렵다는 것과 중첩으로 인한 복잡도가 증가하는 것이다.
크게 이 두가지 단점을 해결하기 위해 프로미스가 예전부터 라이브러리로 생겨났고, 이것을 ES6에서는 언어적 차원에서 지원을 하게 되었다.
 

※ 동기(Sync)? 비동기(Async)?

동기는 메소드를 실행시킴과 동시에 반환 값이 기대되는 경우를 동기 라고 표현하고 그렇지 않은 경우에 비동기 라고 한다. 메소드를 실행시킴과 동시에 반환 값이 기대되는 경우라는 말은 위에서 아래로 순차적으로 진행된다는 말이다. 
비동기의 경우 blocking이 되지 않고 이벤트 큐에 넣거나 백그라운드 스레드에게 해당 task를 위임하고 바로 다음코드를 실행하기 때문에 기대되는 값이 바로 반환되지 않는다. 
● 비동기 처리 사례
console.log("first");
 
setTimeout(function(){
    console.log("third")
}, 3000);
 
console.log("second");
setTimeout()을 통해 3초후에 thrid가 찍히도록 만들어 주어 순차적으로 위에서부터 아래로 first, third, second로 호출되지 않고 first, second, third 순서로 log가 찍히도록 한다.
 

※ Promise 기본 형태

const wait = (param) => new Promise((resolve, reject) => {
    if(param){
        resolve("resolveMethod");
    }else{
        reject("rejectMethod");
    }
});
 
console.log("Promise Start");
 
let param = true;
 
wait(param).then((result) => {
    console.log(result);
}, result => {
    console.log(result);
});
 
console.log("Promise End");
위 함수가 Promise의 기본 형태이다. 위의 소스는 true, false로 성공 실패를 구분하여 resolve와 reject를 호출하는 예제이다. Promise안에서는 비동기 함수를 실행하고 param이 true일 때 resolve(), false일 때 reject를 실행한다. 
간단하게 success -> resolve(), fail -> reject()로 생각하고 함수를 실행할 때 .than(success, fail) 메소드로 수행하면 된다.
 
위의 예시는 화살표 함수로 만들었는데 익숙하지 기존에 사용하던 방식으로 표현을 하면 아래와 같다.
function wait(param){
    return new Promise(function(resolve, reject){
        
        if(param){
            resolve("resolveMethod");
        }else{
            reject("rejectMethod");
        }
    });
}
 
console.log("Promise Start");
 
let param = true;
 
wait(param).then(function(result){
    console.log(result);
}, function(result){
    console.log(result);
});
 
console.log("Promise End");
 
그리고 스크립트를 실행하면 Promise Start, Promise End, resolveMethod 혹은 rejectMethod 순으로 출력된다. 즉 비동기로 실행이 되는걸 알 수 있다.
 

※ Promise 에러 처리

비동기 메소드 중간에 에러가 난다면 catch()를 통해 에러처리가 가능하다.
wait(param).then((result) => {
    console.log(result);
}, result => {
    console.log(result);
}).catch(err =>{
    console.warn(err);
});
 
/*********************************************************/
 
wait(param).then(function(result){
    console.log(result);
}, function(result){
    console.log(result);
}).catch(function(err){
    console.warn(err);
});
 

※ async-await 

함수 앞에 async를 붙이고 비동기 처리 하고싶은 곳에 await을 추가한다.
const wait = (param) => new Promise((resolve, reject) => {
    if(param){
        resolve("resolveMethod");
    }else{
        reject("rejectMethod");
    }
});
 
 
async function fnAsyncAwait(param){
    await wait(param).then((result)=>{
        console.log(result);
    },err => {
        console.log(err);
    }).catch(err => {
        console.warn(err);
    });
}
 
console.log("Promise Start");
 
let param = true;
fnAsyncAwait(param);
 
console.log("Promise End");
위의 소스도 결과값은 Promise Start, Promise End, resolveMethod 혹은 rejectMethod 순으로 출력된다.  
사실 resolve일 때 reject일 때 모두 보여주기 위해 위와 같은 형식으로 계속해서 소스를 작성했는데 굳이 그럴필요 없이 resolve 부분만 구현해도 문제될건 없다.  
그리고 공부를 하면서 setTimeout() 메소드를 이용하여 사용하는 예제가 많이 보이는데 일단은 예제에선 setTimeout() 메소드를 쓰지 않아도 결과가 순차적으로 나오지 않았기 때문에 굳이 사용하지 않았다.
 

※ async 클래스 메소드

async 메소드는 클래스 메소드로도 사용이 가능하다. 그리고 화살표 함수로도 표현이 가능하다.
 
● 일반함수 형태
class asyncTest {
 
    async fnAsyncAwait(){
        try{
            await this.wait();
        } catch (e) {
            console.log(e);
        }
    }
    
    wait(){
        setTimeout(function(){
            console.log("second");
        },1000);
    }
}
 
const asyncObj = new asyncTest();
 
console.log("first");
asyncObj.fnAsyncAwait();
console.log("third");
 
● 화살표 함수형태
class asyncTest {
 
    fnAsyncAwait = async() => {
        try{
            await this.wait();
        } catch (e) {
            console.log(e);
        }
    }
 
    wait = () => {
        setTimeout(function(){
            console.log("second");
        },1000);
    }
}
 
const asyncObj = new asyncTest();
 
console.log("first");
asyncObj.fnAsyncAwait();
console.log("third");
 
모두 로그 출력 순서는 first, third, second 순이다.