본문 바로가기

IT

Effective Typescript - 25 - 비동기 코드에는 콜백 대신 async 함수 사용하기

이번 아이템은 타입스크립트에서의 코드 작성 철학과는 다소 거리가 먼 얘기를 하고 있다.

이 장에서는 자바스크립트의 callback 지옥을 어떻게 하면 극복 할 수 있는지에 대한 설명을 한다.

 

아래는 콜백 지옥 코드에 대한 예제이다.

fetchURL(url1, (res1) =>{
    fetchURL(url2, (res2) =>{
        fetchURL(url3, (res3) =>{
            console.log(1);
        });
        console.log(2);
    });
    console.log(3);
});
console.log(4);

//로그
//4
//3
//2
//1

이런 콜백지옥을 극복하기 위한 수단으로 ES2015에 프로미스(promise) 라는 개념이 추가되었다.

const page1Promise = fetch(url1);

page1Promise.then(res1 =>{
    return fetch(url2);
}).then(res2 =>{
    return fetch(url3);
}).then(res3 =>{
    return fetch(url4);
}).catch(err =>{
    console.log(err);
});

프로미스를 활용하면 콜백 지옥으로부터는 탈출 할수 있다. 또한 아래 예제와 같이 promise를 활용하면 promise.all() 을 활용해 병렬 처리를 할수도 있다.

async function parallelFetch(){
    const promise1 = fetch(url1);
    const promise2 = fetch(url2);
    const promise3 = fetch(url3);

    // 모든 promise가 resolve 되면 promise all의 then이 호출된다.
    // 각각의 fetch는 병렬로 처리된다.
    Promise.all([promise1, promise2, promise3]).then((values) => {
        console.log(values);
    });
}

여기서 promise의 resolve시 then에서 처리하지 않는대신, async 함수내 await를 이용해 promise의 resolve를 기다릴 수 있다.

async function asyncFetch(){
    const promise1 = await fetch(url1); // fetch가 완료될때 까지 기다린다.
    const promise2 = await fetch(url2); // fetch가 완료될때 까지 기다린다.
    const promise3 = await fetch(url3); // fetch가 완료될때 까지 기다린다.
}

async function asyncFetchExceptHandle(){
    try{
        const promise1 = await fetch(url1); // fetch가 완료될때 까지 기다린다.
        const promise2 = await fetch(url2); // fetch가 완료될때 까지 기다린다.
        const promise3 = await fetch(url3); // fetch가 완료될때 까지 기다린다.
    }catch(err){
        console.log(err); // try내 fetch들 에서 reject 발생시 catch 내에서 에러 핸들링이 가능함.
    }
}

위의 코드 작성 패턴을 봤을때, promise를 활용하고 await를 활용하는 방식이 더욱 가독성이 높다는 것에 공감하십니까?

여기서, 타입스크립트 관점에서 봤을때 promise + await 를 이용하는 것이 타입 추론에 더욱 유리하다고 한다.

async function parallelFetch(){

    // 모든 promise가 resolve 되면 promise all의 then이 호출된다.
    // 각각의 fetch는 병렬로 처리된다.
    const [res1, res2, res3] = await Promise.all([fetch(url1), fetch(url2), fetch(url3)]);
}

그리고 async 함수는 항상 Promise를 반환한다.

const get_number = async () => {
    return 1;
}

type get_number_ret = ReturnType<typeof get_number>;
// get_number_ret 타입은 Promise<number>