이벤트루프 / callback지옥과 Promise, async/await

이재홍·2022년 1월 30일
0

이벤트루프

자바스크립트는 기본적으로 동기적으로 실행되지만
일부 함수나 외부 API의 요청들에선 비동기실행이 됩니다.

이때 비동기 실행되는 코드는 순차적으로 실행되는 콜스택(호출스택)에서 옮겨져
백그라운드(Web API)에서 동시에 따로 실행되며 실행이 먼저 끝나는대로 태스크 큐로 이동됩니다.
예를들어 setTimeout(() => 1, 1000) 함수는 콜스택에서 실행되면 백그라운드로 옮겨져 1초뒤에 태스크 큐로 옮겨집니다.

태스크 큐에서는 동기적인 실행들이 모두 끝나 콜스택이 모두 비게되면 콜스택으로 우선순위(Promise > timer 등)에 따라 함수를 콜스택으로 옮겨주고 콜스택에서 최종적으로 함수가 실행되며 종료하게 되는데 이러한 비동기실행의 흐름을 이벤트루프(libuv)가 해줍니다.

callback hell

비동기 실행의 경우 백그라운드에서 따로 실행되기에 콜백함수로 보낸 결과를 다시 재사용 하기 위해서는 그 함수내에서 다시 함수를 호출하는 방법을 사용해야 합니다.

콜백 지옥은 JavaScript를 이용한 비동기 프로그래밍시 발생하는 문제로서, 함수의 매개 변수로 넘겨지는 콜백 함수가 반복되어 코드의 들여쓰기 수준이 감당하기 힘들 정도로 깊어지는 현상을 말합니다.

Promise

Promise는 비동기 실행후 콜백함수로 받을 결과값은 따로 빼주어 이 문제를 해결했습니다.
then으로 프로미스의 결과값을 받고 그 결과값으로 다시 프로미스의 결과값을 담아 프로미스로 반환할 수 있습니다.

Promise는 내용은 실행되었지만 결과는 then을 통해 따로 반환할 수 있는 객체로
수행중일 때는 pending, 완료시 fulfilled, 실패시 rejected 상태가 됩니다.

Promise 생성

비동기 실행은 요청이 실패할 가능성이 항상 있기에 에러처리를 해주어야 하는데
그래서 Promise 객체를 만들 때는 resolve와 reject 콜백함수를 매개변수로 받아
성공시 resolve를 호출하여 then으로
실패시 reject를 호출하여 catch로 이동해 에러를 잡을 수 있습니다.

const promise = new Promise((resolve, reject) => { // 프로미스는 생성될때 자동 실행된다.
  setTimeout(() => {
    if (true) {      
    	resolve('성공결과');
    } else {
    	reject(new Error('요청 거부')); // 에러객체, 에러발생시 콜스택 정보등이 들어있다.
    }
  }, 1000)
});

promise
  .then(value => {
    console.log(value); // '성공결과'
  }) // then은 새로운 Promise를 반환하며 return값은 성공시(fulfilled) 다음 then의 결과값으로 들어감
  .catch(err => { 
    console.log(err); // Error: '요청거부'
  })
  .catch(err => {
    console.log(err); // 앞단계 프로미스(여기선 catch)의 결과값(예외발생시 실행)
  })
  .finally(() => {
    console.log('항상 실행') // 성공,실패 관계없이 실행
  })
  • Promise.allSettled(배열안에 프로미스)로 여러개의 프로미스를 동시에 병렬로 실행하여 각각의 요청마다 성공과 실패를 구분할 수도 있습니다.

async/await

async와 await을 이용해 async 함수내에서 동기적인 처리와 비슷하게 작성할 수 있습니다.

await을 Promise의 앞에 적게 되면 결과값을 받기 전에는 밑에 코드가 실행되지 않습니다.
async 함수로 만들어줘야 하며 async 함수는 Promise를 반환합니다. (리턴값은 결과값(resolved))

  • 노드10부터 for await(변수 of 프로미스배열)으로 여러 프로미스를 동시에 처리할수도 있습니다.
(async () => {
    for await (promise of [promise1, promise2]) {
        console.log(promise) // resolve된 프로미스의 결과값
    }
}

에러 발생시 try{} catch{} 구문으로 예외처리할 수 있습니다.

async function a() {
  throw 'error'
}

async function b() {
  try {
    await a()
  } catch (err) {
    console.log(err) // error
  } 
}

b()

0개의 댓글