Promise 간단 사용 설명서

2020. 11. 29

프로미스(Promise)

비동기 작업을 순차적으로 진행할 수 있도록 도와준다. ES6부터 추가되었다.

프로미스 객체 생성

생성자를 통해 프로미스 객체를 만들 수 있다. 생성자의 인자로는 executor 라는 함수를 사용한다.

new Promise(/* execuotor*/)

executor

여기서 executor는 resolvereject를 인자로 가진다. 이 두가지는 함수로서 resolve(), reject()로 표현된다.

new Promise(/* execuotor*/ (resolve, reject) => {})

pending(대기)

생성자를 통해서 프로미스 객체를 만드는 순간 pending 상태가 된다.

new Promise((resolve, reject) => {})

위 현상이 pending 현상이다.

fulfilled(이행)

executor 함수 인자 중 하나인 resolve 함수를 실행하면 fulfilled 상태가 된다.

new Promise((resolve, reject) => {
  // pending
  // do something..
  // 처리 완료
  resolve() // fulfilled 상태
})

비동기 코드를 처리하고 resolve()를 실행하면 이행 상태가 된다.

예를들어 객체가 1000ms 후에 fulfilled 상태로 돌입하는 코드는 어떨까?

new Promise((resolve, reject) => {
  // pending 상태..

  setTimeout(() => {
    resolve() // fulfilled 상태
  }, 1000) // 1초 후
})

먼저 Promise객체를 생성하고 pending 상태에서 1초 후 resolve()를 호출하여 fulfilled 상태로 만들어준다.

rejected(거부)

executor 함수 인자 중 하나인 reject 함수를 실행하면 rejected 상태가 된다.

new Promise((resolve, reject) => {
  reject() // rejected 상태
})

프로미스 객체 사용

위에서는 프로미스 객체를 생성하는 법을 배웠고 이제 사용하는 방법에 대해 알아보자

const p = new Promise((resolve, reject) => {
  // pending 상태..

  setTimeout(() => {
    resolve() // fulfilled 상태
  }, 1000) // 1초 후
})

p.then(
  /* CallBack */ () => {
    console.log('1000ms 후에 fulfilled 됩니다.')
  },
)

Promise 객체를 p에 담고, resolve 된 시점인 fulfilled 상태에서 .then으로 넘어가게 되고 .then 안에 있는 콜백 함수가 실행된다. 즉 이 코드는 .then이 1초 후에 실행되게 될 것이다.

그렇다면 reject는 어떻게 사용될까?

const p = new Promise((resolve, reject) => {
  // pending 상태..

  setTimeout(() => {
    reject() // rejected 상태
  }, 1000) // 1초 후
})

p.then(
  /* CallBack */ () => {
    console.log('1000ms 후에 fulfilled 됩니다.')
  },
).catch(() => {
  console.log('1000ms 후에 rejected 됩니다.')
})

.catch를 통하여 처리된다.

함수 내 사용

then, catch를 설정하는 시점을 명확히 하고, 함수의 실행과 동시에 Promise 객체를 만들면서 pending이 시작되도록 하기 위함이다.

function p() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    }, 1000)
  })
}

p()
  .then(() => {
    console.log('1000ms 후에 fulfilled 됩니다.')
  })
  .catch(() => {
    console.log('1000ms 후에 rejected 됩니다.')
  })

executor 인자 전달

executor의 resolve, reject 함수를 실행할 때 인자를 넣어주면 then, catch의 콜백 함수의 인자로 받을 수 있다.

function p() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve('hello')
      //   reject("error");
    }, 1000)
  })
}

p()
  .then((message) => {
    console.log('1000ms 후에 fulfilled 됩니다.', message) // 1000ms 후에 fulfilled 됩니다. hello
  })
  .catch((reason) => {
    console.log('1000ms 후에 rejected 됩니다.', reason) // 1000ms 후에 fulfilled 됩니다. error
  })

인자를 받아와 사용할 수 있게 되었다. 하지만 실무에서는 대부분 reject를 콜백 시 Error 객체를 넘겨주게 된다.

function p() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('bad'))
    }, 1000)
  })
}

p()
  .then((message) => {
    console.log('1000ms 후에 fulfilled 됩니다.', message) // 1000ms 후에 fulfilled 됩니다. hello
  })
  .catch((error) => {
    console.log('1000ms 후에 rejected 됩니다.', error) // Error: bad
  })

최종 실행

fulfilled 되거나 reject 된 후에 최종적으로 실행할 것이 있다면, .finally() 를 설정하고, 함수를 인자로 넣는다.

function p() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      reject(new Error('bad'))
    }, 1000)
  })
}

p()
  .then((message) => {
    console.log('1000ms 후에 fulfilled 됩니다.', message) // 1000ms 후에 fulfilled 됩니다. hello
  })
  .catch((error) => {
    console.log('1000ms 후에 rejected 됩니다.', error) // Error: bad
  })
  .finally(() => {
    console.log('end') // end
  })

then, catch 실행 여부와 관계없이 모든게 끝나면 최종적으로 실행한다.

여러개의 프로미스 객체

all

프로미스 객체를 여러개 생성하여 배열로 만든 후 인자로 넣고 Promise.all을 실행하면,
배열의 모든 프로미스 객체들이 fulfilled 되었을 때, then의 함수가 실행된다. 그리고 resolve 인자값을 배열로 돌려준다.

function p(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(ms)
    }, ms)
  })
}

Promise.all([p(1000), p(2000), p(3000)]).then((messages) => {
  console.log('모두 fulfilled 된 상태로 실행됩니다.', messages) // 모두 fulfilled 된 상태로 실행됩니다. (3) [1000, 2000, 3000]
})

배열로 넘긴 Promise 객체를 then으로 받아 배열로 돌려준 것을 확인할 수 있다.

race

프로미스 객체를 여러개 생성하여 배열로 만든 후 인자로 넣고 Promise.race을 실행하면,
배열의 모든 프로미스 객체들 중가장 먼저 fulfilled 된 것으로 then이 실행되고, then의 함수의 인자로 가장 먼저 fulfilled 된 프로미스 객체의 resolve 인자값을 돌려준다.

function p(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(ms)
    }, ms)
  })
}

Promise.race([p(1000), p(2000), p(3000)]).then((message) => {
  console.log('가장 빠르게 fulfilled 된 것이 실행됩니다.', message) // 가장 빠르게 fulfilled 된 객체가 실행됩니다. 1000
})