[Javascript] Promise/Async/Await

윤재열·2022년 11월 13일
0

Javascript

목록 보기
4/4
post-custom-banner

async / await

  • JS를 사용하다보면 비동기 방식을 많이 사용하게 됩니다.
  • 비동기 호출 후 이를 처리하는 콜백 함수의 개념은 매우 중요합니다.
  • 즉 , 비동기 프로그래밍에서 콜백함수는 반드시 사용해야 하는 부분입니다.
  • 그러나 콜백 함수가 깊어지면 코드가 복잡해지게 되므로 ES6 에서 Promise를 도입하였습니다.

Promise 란?

Promise객체는 비동기 계산을 위해 사용됩니다. promise아직은 아니지만 나중에 완료될 것으로 기대되는 연산을 표현합니다.

  • Promise는 단어 그대로 나중에 연산하기 위한 "약속" 같은 개념입니다.

  • Promise는 비동기 요청에 대하여, 비동기 실행이 완료된 후 결과 값 또는 실패의 이유로 콜백 함수로 전달합니다.

  • 따라서 비동기 실행이 정상적으로 되었는지, 오류가 발생하였는지 알 수 있도록 상태가 정의되어 있어야 합니다.

    • 상태
      • fulfilled : 비동기 동작 정상 완료
      • rejected : 비동기 동작 중 에러 발생
  • 다음은 promise를 사용하는 간단한 예제입니다.

let lab = function(bool){
    return new Promise(function(resolve, reject){
        setTimeout( function(){
            if(bool){
                resolve("fulfilled 상태입니다. then으로 연결됩니다.");
            }
            else{
                reject("rejected 상태입니다. catch로 연결됩니다.");
            }
        }, 1000)
    })
}

lab(true)
.then( function(result){
    console.log(result);
})
.catch( function(err){
    console.log(err)
})
  • 먼저 Promise 객체를 생성하는 부분입니다.
  • Promise 생성자 함수에 콜백함수를 정의합니다.
    • 매개변수로 resoleve,reject를 정의합니다.
  • 처리하는 로직상에서 비동기 처리가 정상적으로 수행되면 resolve() 함수를 호출하고, 실패한다면 reject() 함수를 호출합니다.

Rromise 생성자의 prototype에는 then(),catch() 메서드가 존재합니다.

  • then() 메서드는 호출한 Promise가 resolve()를 호출하였을때 반환값을 콜백함수로 받아주며, 이때 새로운 Promise를 반환합니다.

  • catch() 메서드는 호출한 Promise가 reject()를 호출했을 때 반환 값을 콜백함수로 받아주며, 이 때도 새로운 Promise를 반환합니다.

  • 위의 예제 처럼 lab() 함수를 호출할 때 true를 인자로 전달하였기 때문에 비동기 함수인 setTimeout()에서 resolve(0메서드가 실행되기 때문에 fulfilled상태입니다.

    • 만약 여기서 lab(false);로 함수를 호출하면 catch()가 실행됩니다.
  • 즉, then()과 catch()는 동시에 수행되지 않습니다.

Promise의 메서드 종류

1. all
  • all()메서드를 호출할 때, 인자로 여러 Promise 들을 넘겨줍니다.
  • 모든 promise가 fulfiled 되거나, promise가 하나라도 rejected 된경우 promise가 하나라도 jected된경우 promise를 반환합니다.
    • 모든 promise가 fulfiled 되면 배열로써 결과를 전달하고,
    • promise가 하나라도 jected된 경우 rejected된 이유를 반환 합니다.
var promise1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 2000, "promise1");
});
var promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 1000, "promise2");
});

Promise.all([promise1, promise2]).then(function(value) {
    console.log(value);
});
  • promise1 , promise2 모두 resolve() 메서드를 호출했으므로, 모든 promise가 fulfiled 되었으니 모든 결과는 배열이 됩니다.
2. race
  • race() 메서드를 호출할 때, 인자로 여러 Promise들을 넘겨줍니다.
  • fulfiled, rejected 상관없이 처음으로 종료된 Promise를 반환합니다.
var promise1 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 2000, "promise1");
});
var promise2 = new Promise(function(resolve, reject) {
    setTimeout(resolve, 1000, "promise2");
});

Promise.race([promise1, promise2]).then(function(value) {
    console.log(value);
});
  • 위 예제에서 promise1은 setTimeout이 2초이고, promise2 는 1초 입니다.
  • 즉, 비동기 실행인 setTimeout이 promoise2보다 더 빨리 종료되므로 결과는 promise2가 출력됩니다.

하지만 Promise를 사용해도 여전히 코드가 복잡합니다.그래서 ES8 에서 async와 await를 도입하였고, 덕분에 비동기 코드를 동기적으로 깔끔하게 처리할 수 있게 되었습니다.

  • 그렇다고 해서 항상 async/await가 옳다고 할 수 없습니다.

    • 콜백의 깊이가 깊지 않을 때는 작성하기 간편한 콜백함수를 호출하거나,Promise를 사용하는 것이 더 나은 방법일 수도 있습니다.
    • async/await는 Promise를 사용하기 때문에 Promise를 알아야하고, async/await이 할 수 없는 동작을 Promise로 해결할 수 있는 경우도 있습니다.

async / await 키워드

1. 기본 문법

async funtion foo(){
  await someAsyncFunction(){..}
  await anotherAsyncFunction(){...}
}
  • 함수 이름 앞에 async 키워드
  • 호출할 비동기 함수 앞에 await 키워드를 사용합니다.

핵심은 await 이며 , async는 단지 선언용입니다.
즉 , 함수 앞에 async가 선언되어 있어야만 await 키워드를 사용합니다.

  • 위의 예제는 someAsyncFunction, anotherAsyncFunction 두 함수가 비동기 코드일 지라도 async/await 가 적용되면, 항상 someAsncFunction -> anotherAsyncFuntion 순서대로 함수가 실행 됩니다.
  • 이처럼 비동기 코드를 동기적을 수행하게 해주는 것이 async / await 입니다.
  • 이 때 async / await 는 Promise 방식을 사용하기 때문에 someAsyncFunction 과 anotherAsyncFunction 함수는 Promise를 리턴해주어야 합니다.

비동기 함수가 Promise 를 사용하지 않는 예제(async/await 적용이 안된 예제)

async functino test(){
  await foo(1,2000);
  await foo(2,500);
  await foo(3,1000);
}
function foo(num,sec){
  setTimeout(function(){
    console.log(num);
  },sec);
}
test();
// 2, 3, 1
  • 위의 예제는 비동기함수 setTimeout() 함수를 호출할 때 async / await 를 사용해서 test() 함수 실행 결과 1,2,3 순서대로 응답되기를 기대한 것입니다.
  • 그러나 1,2,3 순서대로 호출되지 않고, 이벤트가 끝난 순서대로 호출됩니다.
  • 즉 async / await 으로 비동기 코드를 동기식으로 바꾸고 싶었지만 비동기 코드가 그대로 수행 됩니다.

비동기 함수에 Promise를 사용하는 예제(async / await 이 적용되어 동기적으로 출력)

async function test(){
  await foo(1,2000);
  await foo(2,500);
  await foo(3,1000); 
}  
function foo (num,sec){
  return new Promise(function(resolve,reject){
    setTimeout(function(){
      console.log(num);
      resolve('async는 Promise 방식을 사용합니다');
    },sec);
  });
}
// 1,2,3
  • 이번에는 foo() 함수가 Promise를 반환하도록 수정했습니다.
  • 그랬더니 의도대로 비동기 함수들이 순서대로 1,2,3 을 응답하였습니다.
  • 그 이유는 await는 Promise를 받기 때문입니다.
  • 즉, 비동기 함수 foo() 실행 결과 Promise를 리턴하면 await는 이를 받아서 동기적으로 수행하게 해줍니다.
  • 이로써 async / await는 Promise 방식을 따릅니다.

test 함수를 Promise로 바꾸기

  • 이번에는 async /await 와 Promise 중 어느 것이 깔끔한 표현인지 누가 더 가독성이 좋은지 비교학 위의 예제. test() 함수를 Promise로 바꿔 표현해보도록 하겠습니다.
function test(){
    foo(1,2000)
    .then( () => {
        return foo(2,500)
    })
    .then( () => {
        return foo(3,1000)
    })
}

function foo(num, sec){
    return new Promise(function(resolve, reject){
        setTimeout( function(){
            console.log(num);
            resolve("async는 Promise방식을 사용합니다.");
        }, sec);
    });
}
test();
  • 결과는 " 3) 비동기 함수에 Promise를 사용하는 예제" 와같이 1, 2,3 이 출력됩니다.

5. async / await / Promise 섞어쓰기

  • 이번에는 async/ await 이 promise 를 사용한다는 점을 이용하여 Promise와 섞어서 사용해 봅니다.
async function test(){
    await foo(1, 2000)
    await foo(2, 500).then( () => {
        foo(3, 1000)
    })
}

function foo(num, sec){
    return new Promise(function(resolve, reject){
        setTimeout( function(){
            console.log(num);
            resolve("async는 Promise방식을 사용합니다.");
        }, sec);
    });
}
test();
  • async/await와 Promise 는 동시에 사용이 가능합니다.
    그 이유는 async/ await이 Promise를 반환하기 때문입니다.
profile
블로그 이전합니다! https://jyyoun1022.tistory.com/
post-custom-banner

0개의 댓글