Frontend Develop/JavaScript

자바스크립트 비동기 API - Promise

효니킴 2023. 6. 25. 22:51

📝 Promise API

프로미스 API는 비동기 API 중 하나이다. 정리하자면,

  • 태스크 큐가 아닌 잡큐(Job queue 혹은 microtask queue)를 사용한다.
  • 잡큐의 경우 setTimeout() 등의 API가 사용하는 태스크 큐보다 우선순위가 높다. 
  • Promise 객체는 비동기 처리 중에 pending 상태를 표현할 수 있다.
  • Promise.prototype.then 체인을 이용해 비동기 처리 순서를 강제할 수 있다.
  • Promise.prototype.finally()메서드는 Promise가 settled되었을 때 항상 호출된다.

 

Promise API 예시

예시 코드를 보면 setTimeout과 Promise 는 순차적으로 처리되는 것이 아니라 얽혀있다. 

실행결과를 보면 프로미스 처리가 setTimeout보다 앞선 다는 것을 확인할수 있다.

 

📝 Promise

  • 프로미스는 비동기 작업을 표현하는 자바스크립트 객체이다.
  • 비동기 작업의 진행, 성공, 실패, 상태를 표현한다.
  • 그리고 비동기 처리의 순서를 표현할 수 있다. 

프로미스의 상태는 new Promise()를 생성하거나 또는 프로미스를 반환하는 비동기 fetch API를 사용할 수 있다. 

비동기 작업이 먼저 실행될 경우 Pending(진행)상태가 되고, Pending 상태에서 성공(Fullfiled or resolve) 혹은 실패(rejected) 상태를 표현할수 있다. Pending 상태에서 작업이 끝나면 성공과 실패상태를 통틀어서 settled라고 표현한다.

 

프로미스 내부에는 상태를 나타내는 플래그가 있고, 성공 시 .then 메서드 호출되고, 실패의 경우 .catch 메서드가 호출된다. 여기서 .then의 경우 성공, 실패 콜백을 2개 넣을 수있고 .then(성공, 실패), 또는 성공 콜백만 넣을수 도 있다. 

 

📝 Promise 생성자

  • 프로미스 생성자는 new Promise(callback)으로 프로미스를 생성할수 있다.
  • callback 함수의 경우 executor 라고 표현한다. 이 executor 함수는 (resolve, reject) 두 인자를 받아서 내부에서 reject 혹은 resolve 호출을 함으로써 성공 실패를 조작할수 있다.
  • 프로미스가 성공 했을 때 resolve를 호출하고
  • 프로미스가 실패 했을 때 reject를 호출한다.

 

*아래 코드에서 Math.random()은 0~1까지 호출되는 함수이다.

let promise = new Promise((resolve, reject) => {
  if(Math.random() < 0.5){
    return reject("실패")
  }
  resolve(10)
})

 

📝 Promise 메서드

성공 또는 실패 상황이 되었을 때 .then(), .catch(), .finally() 메서드를 통해 각 상황에 맞게 함수에 넣어줄수 있다.

 

예시로 resolve("data") 를 받았을 경우 .then , .catch, .finally 메서드로 처리를 할수 있다.

  • then() 메서드에 성공 시 실행할 콜백 함수를 인자로 넘긴다.
  • catch() 메서드에 실패했을 때 실행할 콜백 함수를 인자로 넘긴다.
  • finally() 메서드는 성공/실패 여부와 상관없이 모두 실행할 콜백함수를 인자로 넘긴다.
  • then(callback1, callback2)로 callback1는 성공, callback2는 실패 메서드를 인자로 넘길 수 있다.

 

📝 Promise 메서드 체인

메서드가 줄줄이 이어진다는 의미로 메서드 체이닝이라고 부른다.

  • then/catch 메서드가 또 다른 프로미스를 리턴해서 비동기 코드에 순서를 부여할수 있다.
  • 동일한 객체에 메서드를 연결할 수 있는 것을 메서드 체이닝(chaining)이라고 한다.
  • 함수를 호출한 주체가 함수를 끝낸 뒤 자기 자신을 리턴하도록 하여 구현을 한다.

예시로 new Promise()로 프로미스를 생성을 했을 때, 생성자는 객체를 초기화하고, 인스턴스를 return을 한다.

내부적으로는 return this코드가 생략이 되어있다. .then 메서드 또한 내부적으로 retrun this가 숨겨져 있는 코드이다.

 

결론적으로 자기 자신을 계속 리턴을 하는 함수가 있기 때문에 new Promise() 객체에 있는 .then() 혹은 .catch() 메서드가 호출이 될수 있는 것이다.

promise
  .then(data => {
    return fetUser(data)
  })
  .then(user => {
    console.log('User:', user)
  })
  .catch(e => {
    console.log("실패:", e)
  })

또한 then, catch 메서드가 항상 프로미스를 리턴하는 것은 아니다. 예시로 data = 10 이 있다면 다음 then에서 data = 10을 받을 수 있다. 만일에 fetchUser라는 함수가 있고, 그 함수가 프로미스를 리턴하는 함수라면 그다음 then이 호출되기 전에 프로미스가 종료 될때 까지 기다린다. 이와 같은 과정은 비동기함수에 순서를 주는 것이다.

 

📕 Promise.resolve, Promise.reject

프로미스는 정적인 메소드가 존재한다. Promise 클래스명에 상태를 바로 사용할 수 있는 static함수이다.

  • Promise.resovle로 호출하면 바로 성공한 프로미스 객체를 반환한다.
  • Promise.reject로 호출하면 실패한 프로미스를 반환한다.
  • 인위적으로 Promise 메서드 체인을 만들 수있다. 즉, 꼭 프로미스를 리턴하는 함수를 만들어서 그 함수를 호출하지 않아도 바로 성공 혹은 실패 프로미스를 호출할수 있다.
  • 비동기 코드로 진행해야하는 상황 등에는 유용하게 사용할 수 있다.

📕 Promise.all

  • Promise.all 메소드 또한 정적메소드이다. Promise.all을 호출 시에 배열을 넘긴다.
  • Promise.all은 배열에 담긴 모든 프로미스가 resolve 될때까지 혹은 reject될 때까지 기다린다.
  • 모든 프로미스가 resolve한 데이터들을 배열에 넘겨서 반환을 한다. 하나라도 실패시 실패이유를 반환한다.
Promise.all([
  promise1,
  promise2,
  promise3
])
  .then(valuse => {
    console.log("모두 성공:", valuse)
  })
  .catch(e => {
    console.log("하나라도 실패:",e)
  })