Promise는 자바스크립트에서 제공하는 비동기를 간편하게 처리할 수 있게 도와주는 object(객체)이다.
정해진 긴 시간의 기능을 수행하고 정상적으로 수행이 됐다면 성공의 메세지와 함께 처리된 결과값을 전달, 수행하다가 예상치 못한 문제가 발생했을 경우 에러를 전달한다.
Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타낸다.
Promise는 프로미스가 생성된 시점에는 알려지지 않았을 수도 있는 값을 위한 대리자로,
비동기 연산이 종료된 이후에 결과 값과 실패 사유를 처리하기 위한 처리기를 연결할 수 있다.
프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있다.
다만 최종 결과를 반환하는 것이 아니고,
미래의 어떤 시점에 결과를 제공하겠다는 '약속'(프로미스)을 반환한다.
Promise는 다음 중 하나의 상태를 가진다.
대기(pending): 이행하지도, 거부하지도 않은 초기 상태.
이행(fulfilled): 연산이 성공적으로 완료됨.
거부(rejected): 연산이 실패함.
실행 과정 : pending => fulfilled or rejected
대기 중인 프로미스는 값과 함께 이행할 수도, 어떤 이유(오류)로 인해 거부될 수도 있다.
이행이나 거부될 때, 프로미스의 **then** 메서드에 의해 대기열(큐, Queue)에
추가된 처리기들이 호출된다. 이미 이행했거나 거부된 프로미스에 처리기를 연결해도 호출되므로,
비동기 연산과 처리기 연결 사이에 경합 조건은 없습니다.
=> 이미 실행된 프로미스나 거부된 프로미스들 사이의 충돌(순서에 있어서)은 고려하지 않아도 된다.
출처 : [MDN] - promise
딱 2가지 개념만 잘 이해해두면 Promise를 이해할 때 전혀 무리가 없다.
promise 객체를 생성할 때는 excutor 라고 하는 콜백 함수를 전달해줘야 한다. 그 콜백 함수 안에서는 resolve와 reject 두 가지의 콜백함수를 받는다.
굳이 굳이 표현을 하자면
(resolve, reject) => { console.log('doing something'); }
아래의 예제 코드를 예로 들었을 때 위의 코드(콜백 함수)가 excutor 인 셈이다.
// 1. Producer => promise 객체
const promise = new Promise((resolve, reject) => {
// Promise Object 생성 -> doing some heavy work
console.log('doing something');
})
위에서 말하는 heavy work는 다음과 같다.
=> 시간이 오래 걸리는 작업
이 작업들을 만약 동기로 진행하면? 파일을 읽고 데이터를 받아오는 동안 다음 코드(작업)이 실행이 안된다. 계속 기다려야 함
=> 프로미스를 만들어서 비동기로 처리한다.
위의 예제 코드를 실행할 경우 console이 바로 찍힌다.
=> 프로미스 객체가 만들어지는 순간에 우리가 전달한 excutor라는 콜백 함수가 바로 실행이 된다.
When new Promise is created, the excutor runs automatically
만약 프로미스 안에 네트워크 통신을 하는 코드를 넣었다고 하면? 프로미스 객체를 만드는 순간 바로 네트워크 통신을 수행하게 된다.
만약 사용자가 요구했을 때만 (버튼을 눌렀을 때) 네트워크 요청을 해야 한다면?
// 1. Producer
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('jiwon')
}, 2000);
})
// 2. Consumers : then, catch, finally
promise.then(value => {
console.log(value);
})
setTimeout을 이용해서 네트워크 통신을 했을 때에 발생하는 시간 딜레이를 표현했다.
원하는 기능을 잘 수행했을 때 -> resolve 라는 함수를 호출
데이터를 받아왔는데 사용자의 이름이 jiwon 이더라!
성공적으로 네트워크에서 받아온, 파일에서 읽어온 , 가공한 데이터를 resolve 라는 콜백 함수를 통해 전달한다 => resolve 함수에다가 던져준다
그러면!! 이걸 사용해야지.
어디서? -> consumer 에서
어떻게? -> then, catch, finally 를 이용해서
promise는 어떤 일을 하는데 2초 정도 딜레이가 있고 그 어떤 일을 잘 마무리했을 경우 resolve 콜백 함수를 호출하면서 jiwon을 전달해주는 역할을 한다. (잘 안됐을 경우 -> reject)
consumer는 then , catch, finally를 이용해서 그 값을 전달 받는다.
위의 코드에서 then은
promise가 잘 동작했을 경우~~ => resolve가 호출 됐을 경우~~
이 때의 인자 value는 resolve를 통해 전달받은 데이터, jiwon 이다.
const promise = new Promise((resolve, reject) => {
console.log('doing something');
setTimeout(() => {
reject(new Error('no network'));
}, 2000);
})
promise
.then(value => {
console.log(value);
})
.catch(error => {
console.log(error);
});
resolve -> .then
reject -> .catch
reject 메서드의 경우 promise에서 어떠한 문제가 생겨 수행할 작업이 성공적으로 마무리 되지 않았을 경우에 해당하고 .catch로 빠지게 된다.
헷갈릴 수 있는 부분은 then과 catch 가 위와 같이 엮여있다는 것인데, promise 객체가 then을 호출하면 then은 결국 똑같은 promise 객체를 리턴하기 때문에, 그 리턴된 promise 객체에서 다시 catch를 호출하는 것이다.
const promise = new Promise((resolve, reject) => {
console.log('doing something');
setTimeout(() => {
reject(new Error('no network'));
}, 2000);
})
promise
.then(value => {
console.log(value);
})
.catch(error => {
console.log(error);
})
.finally(() => {
console.log("finally")
});
promise 객체를 만들 때 비동기적으로 수행하고 싶은 코드를 작성하고, 그 작업이 성공적으로 실행됐을 경우 resolve를 호출하게 되고 실패했다면 실패한거랑 왜 실패했는지 error message를 보낸다.
then 과 catch 를 통해 성공한 값 또는 실패한 에러를 받아와서 원하는 방식으로 처리해주면 된다.
최근에 추가된 것이 바로 finally 이다. finally는 성공, 실패 여부랑 상관없이 마지막에 무조건 실행한다. => 성공, 실패 여부랑 상관없이 무조건 실행하고 싶은 작업이 있을 경우 사용한다.