Promise
Promise
는 비동기 작업의 최종 완료 또는 실패를 나타내는 객체
기본적으로 promise는 함수에 콜백을 전달하는 대신에, 콜백을 첨부하는 방식의 객체
약속
(프로미스)을 반환프라미스 객체의 state
, result
프로퍼티는 내부 프로퍼티이다.
state
처음엔 pending
(보류)이었다 resolve
가 호출되면 fulfilled
, reject
가 호출되면 rejected
로 변한다.
대기(pending)
: 이행하지도, 거부하지도 않은 초기 상태.이행(fulfilled)
: 연산이 성공적으로 완료됨.거부(rejected)
: 연산이 실패함.Promise()
생성자Promise
생성자는 주로 프로미스를 지원하지 않는 함수를 감쌀 때 사용한다.
new Promise(resolve,reject)
result
처음엔 undefined
이었다 resolve(value)
가 호출되면 value
로, reject(error)
가 호출되면 error
로 변한다.
resolve
Promise.resolve()
Promise.resolve(value);
reject
Promise.reject()
Promise.reject(reason);
then
then()
메서드는 Promise를 리턴하고 두 개의 콜백 함수를 인수로 받는다.promise.then(
function(result) { /* 결과(result)를 다룹니다 */ },
function(error) { /* 에러(error)를 다룹니다 */ }
);
예시
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve("완료!"), 1000);
});
// resolve 함수는 .then의 성공 여부에 따라 함수(인수)를 실행한다.
promise.then(
result => alert(result), // 성공적으로 이행된 경우, 1초 후 "완료!"를 출력
error => alert(error) // 거부된 경우, 1초 후 "Error: 에러 발생!"을 출력
);
// 작업이 성공적으로 처리된 경우만 다루고 싶다면 .then에 인수를 하나만 전달하면 된다.
let promise = new Promise(resolve => {
setTimeout(() => resolve("완료!"), 1000);
});
promise.then(alert); // 1초 뒤 "완료!" 출력
catch
catch()
메서드는 Promise가 거부될 때 호출할 함수를 예약한다..then(null, errorFunction)
같이 null
을 첫 번째 인수로 전달하면 된다..catch
는 .then
에 null
을 전달하는 것과 동일하게 작동한다.예시
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
// .catch(f)는 promise.then(null, f)과 동일하게 작동한다
promise.catch(alert); // 1초 뒤 "Error: 에러 발생!" 출력
finally
finally()
메소드는 Promise가 처리되면 충족되거나 거부되는지 여부에 관계없이 지정된 콜백 함수가 실행된다.예시
new Promise((resolve, reject) => {
/* 시간이 걸리는 어떤 일을 수행하고, 그 후 resolve, reject를 호출함 */
})
// 성공·실패 여부와 상관없이 프라미스가 처리되면 실행됨
.finally(() => 로딩 인디케이터 중지)
.then(result => result와 err 보여줌 => error 보여줌)
그러나,
finally
는.then(f, f)
과 완전히 같진 않다.
finally
핸들러엔 인수가 없다.
finally
에선 프라미스가 이행되었는지, 거부되었는지 알 수 없다.finally
에선 절차를 마무리하는 ‘보편적’ 동작을 수행하기 때문에 성공·실패 여부를 몰라도 된다.finally
핸들러는 자동으로 다음 핸들러에 결과와 에러를 전달한다.
// result가 finally를 거쳐 then까지 전달된다.
new Promise((resolve, reject) => {
setTimeout(() => resolve("결과"), 2000)
})
.finally(() => alert("프라미스가 준비되었습니다."))
.then(result => alert(result)); // <-- .then에서 result를 다룰 수 있음
// 프라미스에서 에러가 발생하고 이 에러가 finally를 거쳐 catch까지 전달된다.
new Promise((resolve, reject) => {
throw new Error("에러 발생!");
})
.finally(() => alert("프라미스가 준비되었습니다."))
.catch(err => alert(err)); // <-- .catch에서 에러 객체를 다룰 수 있음
Promise chaining
두 개 이상의 비동기 작업을 순차적으로 실행해야 하는 상황에서,
각각의 작업이 이전 단계 비동기 작업을 성공하고 이후 그 결과값을 이용하여,
다음 비동기 작업을 실행해야하는 경우 promise chain을 이용한다.
예시
const promise = doSomething();
const promise2 = promise.then(successCallback, failureCallback);
// 또는
const promise2 = doSomething().then(successCallback, failureCallback);
doSomething(function(result) {
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
지옥의 콜백 피라미드
가 만들어졌다.doSomething().then(function(result) {
return doSomethingElse(result);
})
.then(function(newResult) {
return doThirdThing(newResult);
})
.then(function(finalResult) {
console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback)
모던한 방식으로 접근한다면, 콜백 함수들을 반환된 promise
에 promise chain
을 형성하도록 추가할 수 있다.
then
에 넘겨지는 인자는 선택적(optional)이다.
그리고, catch(failureCallback)
는 then(null, failureCallback)
의 축약이다.
// 화살표 함수로 다음과 같이 표현이 가능하다.
doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => {
console.log(`Got the final result: ${finalResult}`);
})
.catch(failureCallback);
Promise.all()
여러 개의 비동기 작업을 동시에 처리하고 싶을 때 사용
let promise = Promise.all([...promises...]);
Promise.all
은 요소 전체가 프라미스인 배열(엄밀히 따지면 이터러블 객체이지만, 대개는 배열)을 받고 새로운 프라미스를 반환한다.result
가 된다.아래 Promise.all은 3초 후에 처리되고, 반환되는 프라미스의 result는 배열 [1, 2, 3]이 된다.
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000)), // 1
new Promise(resolve => setTimeout(() => resolve(2), 2000)), // 2
new Promise(resolve => setTimeout(() => resolve(3), 1000)) // 3
]).then(alert);
// 프라미스 전체가 처리되면 1, 2, 3이 반환된다.
// 각 프라미스는 배열을 구성하는 요소가 된다.
Promise.all
의 첫 번째 프라미스는 가장 늦게 이행되더라도 처리 결과는 배열의 첫 번째 요소에 저장된다.Promise.all()
이 결과를 반환하기 때문. Promise.all에 전달되는 프라미스 중 하나라도 거부되면, Promise.all이 반환하는 프라미스는 에러와 함께 바로 거부된다.
Promise.all([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000)),
new Promise((resolve, reject) => setTimeout(() => reject(new Error("에러 발생!")), 2000)),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000))
]).catch(alert); // Error: 에러 발생!
Promise.all
전체가 거부되고, .catch
가 실행된다. Promise.all
전체의 결과가 된다.에러가 발생하면 다른 프라미스는 무시된다.
프라미스가 하나라도 거부되면
Promise.all
은 즉시 거부되고, 배열에 저장된 다른 프라미스의 결과는 완전히 무시된다.
이행된 프라미스의 결과도 무시된다.