• [JS] 자바스크립트 비동기 처리(Promise)

    2023. 3. 21.

    by. 지은이: 김지은

    728x90

    자바스크립트에서 콜백함수를 이용하면 비동기 처리를 할 수 있다.

    하지만 위처럼 콜백함수 안에 함수 호출이 반복되어 다른 비동기 작업을 실행해야하는 경우, 콜백함수를 계속 중첩해서 사용하면 코드가 길어지고 복잡해지는 콜백지옥에 빠질 수 있다. 

     

    이러한 문제를 해결하기 위해 Promise가 도입되었다.

     

    1. Promise

    Promise는 콜백함수의 단점을 보완하여 역시 비동기 처리에 사용되는 자바스크립트 객체.

    promise는 작업을 수행하는 함수가 해당 작업의 결과를 알려주기로 '약속'을 한다.

    대기(pending), 이행(fulfilled), 거부(rejected)와 같은 3가지 상태를 가지는데 작업이 완료되지 않은 상태에서는 대기 상태를 유지하고, 작업이 완료되었을 때 해당 결과에 따라 이행 또는 거부 상태로 전환된다.

     

    const promise = new Promise((resolve, reject) => { //executor(resolve, reject)
        setTimeout(() => {
            resolve('완료'); // 성공이면 2초뒤에 완료
        }, 2000)
    });

    promise를 생성하는 방법은 class문법과 같이 new 키워드를 사용해서 promise객체를 생성한다.

    그리고 resolve와 reject 실행함수 즉, 콜백함수를 인자로 받는데 promise가 완료되었을 때 호출된다.

    resolve는 작업이 성공적으로 완료되었음을, reject는 작업이 실패했음을 나타내며 promise는 항상 성공 또는 실패 중 하나의 상태로 전환되기 때문에 resolve, reject 중 하나는 반드시 호출되어야한다.

     

    위 코드는 setTimeout 함수를 사용해서 2초 뒤에 resolve를 호출하며, '완료'를 반환하게 된다.

    지금은 무조건 성공하는 코드만 작성되어있지만, 때에 따라 작업의 완료나 실패 처리를 할 수 있다.

    • resolve(성공 리턴값) -> then으로 연결
    • reject(실패 리턴값) -> catch로 연결
    • finally -> 무조건 실행

     

    then

    then 메서드는 기본적으로 promise를 반환한다.

    작업이 성공했을 때 해당 결과를 받아 처리하는 역할을 한다.

    const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve('완료');
            reject(new Error('에러'));
        }, 2000)
    });
    promise.then(
    	value => {console.log(value)}, // 성공 시 첫번째 함수 실행 '완료' 출력
    	error => {console.log(error)} // 실패 시 두번째 함수 실행 '에러' 출력
    );

    아까는 성공했을 때에 처리만 했지만, 실패했을 때 reject를 호출해서 에러처리를 해주고 에러 객체를 promise에 전달한다.

    이후 then() 메서드를 사용해서 promise 객체에 성공콜백과 실패콜백함수를 등록한다.

    첫 번째 콜백함수는 성공 시에 실행이 되고 두 번째 콜백함수는 실패 시에 실행이 된다.

     

     

    catch

    catch() 메서드는 비동기 처리 혹은 then 메서드 실행 중 에러 발생 시 호출되며 역시 promise를 반환한다.

    const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
            reject(new Error('에러'));
        }, 2000)
    });
    
    promise.catch(
    	error => {console.log(error)} // 2초 뒤에 '에러' 출력
    );

    reject를 호출해서 작업이 실패하게 되며 에러 객체를 promise에 전달한다.

    catch() 메서드는 promise 객체에서 발생한 오류를 처리하기 위한 콜백함수를 등록하고 작업이 실패했을 때 콜백함수가 호출되어 

    에러 메시지가 콘솔에 출력된다.

     

    finally

    finally는 성공 실패 여부 상관 없이 무조건 실행되는 메서드

    promise.finally(() => {
    	console.log('finally')
    }); // 성공이든 실패든 무조건 'finally' 출력 (성공 시 resolve 실행 후 출력)

     

    2. Promise.all

    여러개의 promise 객체들을 받아 모두 성공 시 각 promise의 resolve 값을 배열로 반환한다. (하나라도 실패하면 catch로 넘어감)

    (.allSettled로 실패한 것만 추려낼 수 있음)

     

    const promise1 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('1')
      }, 1000)
    })
    
    const promise2 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('2')
      }, 2000)
    })
    
    const promise3 = new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve('3')
      }, 3000)
    })
    
    Promise.all([
        promise1,
        promise2,
        promise3
    ])
    .then(value => {
    	console.log("모두 성공: ", value)
    })
    .catch(e => {
    	console.log("하나라도 실패: ", e)
    })

     

    3개의 Promise 객체를 생성하고 Promise.all()을 사용해서 배열에 담아 호출한다.

    모든 Promise가 성공적으로 완료한다면 .then이 실행되고, 하나라도 실패한다면 .catch 블록이 실행된다.

     

    Promise 체이닝

    순차적으로 처리해야 하는 비동기 작업이 여러개 있을 때 코드를 더 효율적으로 짜기 위해 promise를 연결하여 사용할 수 있다.

    const number = new Promise((resolve, reject) => {
        setTimeout(() => resolve(1), 1000);
    });
    
    number
        .then(num => num * 2)
        .then(num => num * 3)
        .then(num => num * 4)
        .then(num => console.log(num)); // 24

    이 Promise 객체는 1초후에 1을 반환해서 연산한 값을 출력하며 마지막 then() 블록에서 최종 결과를 콘솔에 출력한다.

     

    Promise를 사용해서 비동기 코드를 처리하는 것은 콜백함수를 중첩해서 사용하는 것보다 훨씬 간편하고 가독성이 좋다.

    그러나 Promise를 직접 연결해서 사용하는 것보다 Async/Await 사용하는 게 더 효과적일 수도 있다.

    Async/Await은 코드를 보다 동기적으로 작성할 수 있도록 해주기 때문에 코드를 이해하고 유지보수하기가 더 쉬워진다.

    댓글