
let promise = new Promise(function (resolve, reject) {
executer(제작 코드 ,'가수')
});
new Promise에 전달되는 함수는 executer(실행자, 실행 함수)라고 한다. executer는 new Promise가
만들어 질 때 자동으로 실행. 결과를 최종적으로 만들어내는 제작 코드를 포함.
resolve 와 reject는 자바스크립트에서 자체 제공하는 콜백이기 때문에
resolve와 reject는 신경 쓰지 않고 executer안 코드만 작성하면 된다.
대신 executer에선 상황에 따라 인수로 넘겨준 콜백 중 하나를 반드시 호출해야 한다.
new Promise 생성자가 반환하는 promise 객체는 다음과 같은 내부 프로퍼티를 갖는다.
let promise = new Promise((resolve, reject) => {
// 프라미스가 만들어지면 executer는 자동으로 실행
// 1초 뒤에 일이 성공적으로 끝났다는 신호가 전달되면서 result는 '완료'가 된다.
setTimeout(() => {resolve('완료'), 1000};
});
위 예제에서 알 수 있는 사실은 두 가지다.
executor '처리'가 시작 된 지 1초 후, resolve('완료')가 호출되고 결과가 만들어진다.
Promise {
state: "Pending",
result: undefined,
}
↓ resolve('완료')
Promise {
state: "fulfiled",
result: "완료",
}
성공적으로 처리되었을 때의 프라미스는 'fulfiled promise(야속이 이행된 프라미스)'라고 불린다.
에러와 함께 약속한 작업을 거부하는 경우,
let promise = new Promise(function(resolve, reject) {
1초 뒤에 에러와 함께 실행이 종료되었다는 신호를 보냅니다.
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
Promise {
state: "Pending",
result: undefined,
}
↓ reject(Error객체)
Promise {
state: "rejected",
result: Error객체,
}
resolved 혹은 rejected 상태의 프라미스는'처리된(settled)' 프라미스라고 부른다.
반대되는 프라미스는 '대기(pending)' 중인 프라미스.
가장 중요하고 기본이 되는 메서드
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) 실행되지 않음
);
첫 번째 함수가 실행
거부된 경우 두 번째 함수가 실행
let promise = new Promise(function (resolve, reject) {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
reject 함수는 .then의 두 번째 함수를 실행합니다.
promise.then(
result => alert(result), 실행되지 않음
error => alert(error) 1초 후 "Error: 에러 발생!"을 출력
);
에러만 발생한 상황을 다루고 싶다면 .then(null, errorHandlingFn)와 같이 첫 번째 인수에 null을
전달하면 된다. .catch(errorHandlingFn)을 써도 되는데, then에 null울 전달하는 것과 동일하게 작동한다.
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
.catch(f)는 promise.then(null, f)과 동일하게 작동합니다
promise.catch(alert); 1초 뒤 "Error
프라미스가 처리되면(이행 또는 거부) 항상 실행된다는 점에서 .then(f, f)과 유사하다.
new Promise((resolve, reject) => {
// 시간이 걸리는 어떤 일을 수행하고, 그 후 resolve, reject를 호출함
})
//성공·실패 여부와 상관없이 프라미스가 처리되면 실행됨
.finally(() => 로딩 인디케이터 중지)
.then(result => result와 err 보여줌 => error 보여줌)
finally는 then(f, f)과 완전히 같지는 않다.
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000); //(*)
}).then(function(result) { //(**)
alert(result); 1
return result \* 2;
}).then(function(result) { //(***)
alert(result); 2
return result \* 2;
}).then(function(result) {
alert(result); 4
return result \* 2;
});
프라미스 체인이 가능한 이유는 promise.then을 호출하면 프라미스가 반환되기 때문이다.
핸들러가 값을 반환할 때, 이 값이 프라미스의 result가 된다.
let promise = new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
});
promise.then(function(result) {
alert(result); 1
return result \* 2;
});
promise.then(function(result) {
alert(result); 1
re turn result \* 2;
});
promise.then(function(result) {
alert(result); 1
return result \* 2;
});
위 예시의 핸들러는 result를 순차적으로 전달하지 않고 독립적으로 처리한다.
.then(handler)에 사용된 핸들러가 프라미스를 생성하거나 반환하는 경우 이어지는 핸들러는 프라미스가 처리될 때까지 기다리다 처리가 완료되면 그 결과를 받는다.
new Promise(function(resolve, reject) {
setTimeout(() => resolve(1), 1000);
}).then(function(result) {
alert(result); //1
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
})
}).then(function(result) { (\*\*)
alert(result); //2
return new Promise((resolve, reject) => {
setTimeout(() => resolve(result * 2), 1000);
}).then(function(result) {
alert(result); // 4
});
프라미스가 거부되면 제어 흐름이 제일 가까운 rejection 핸들러로 넘어가기 때문에 프라미스 체인을 사용하면 에러를 쉽게 처리할 수 있다.
fetch(url) // 거부
.then((response) => response.json())
.catch((err) => alert(err)); // TypeError: failed to fetch
.catch는 첫번째 핸들러일 필요는 없고 하나 혹은 여러 개의 .then 뒤에 올 수 있다.
프라미스 executor와 프라미스 핸들러 주위엔 보이지 않는(암시적) try...catch가 있다. 예외가 발생하면 rejcet처럼 다룬다.
new Promise((resolve, reject) => {
throw new Error("에러 발생!");
}).catch(alert); // Error: 에러 발생!
위 예시는 아래 예시와 똑같이 동작한다.
new Promise((resolve, reject) => {
reject(new Error("에러 발생!"));
}).catch(alert);
executer함수뿐만 아니라 핸들러 안에서 암시적 try...catch는 스스로 에러를 잡고, 에러를 거부 상태의 프라미스로 변경시킨다.
체인 마지막의 .catch는 try...catch와 유사한 역할을 한다. .then핸들러를 원하는만큼 사용하다 마지막에 .catch 하나만 붙이면 발생한 에러를 처리할 수 있다.
// 실행 순서: catch -> then
new Promise((resolve, reject) => {
throw new Error("에러 발생!");
})
.catch(function (error) {
alert("에러가 잘 처리되었습니다. 정상적으로 실행이 이어집니다.");
})
.then(() => alert("다음 핸들러가 실행됩니다."));
.catch 블록이 정상적으로 종료되었기 때문에 다음 성공 핸들러 .then이 호출된다.
또 다른 예시로 (*)로 표시한 핸들러에서 에러를 잡는데, 여기서는 에러를 처리하지 못하기 때문에 에러를 다시 던진다.
// 실행 순서: catch -> catch
new Promise((resolve, reject) => {
throw new Error("에러 발생!");
})
.catch(function (error) {
// (*)
if (error instanceof URIError) {
// 에러 처리
} else {
alert("처리할 수 없는 에러");
throw error; // 에러 다시 던지기
}
})
.then(function () {
/* 여기는 실행되지 않습니다. */
})
.catch((error) => {
// (**)
alert(`알 수 없는 에러가 발생함: ${error}`);
// 반환값이 없음 => 실행이 계속됨
});
아래 예시처럼 에러가 발생했는데 .catch문을 추가하지 않아 에러를 처리하지 못하면 무슨 일이 생길까?
new Promise(function () {
noSuchFunction(); // 존재하지 않는 함수를 호출하기 때문에 에러가 발생함
}).then(() => {
// 성공상태의 프라미스를 처리하는 핸들러. 한 개 혹은 여러 개가 있을 수 있음
}); // 끝에 .catch가 없음!
에러가 발생하면 프라미스는 거부상태가 되고, 실행 흐름은 가장 가까운 rejection 핸들러로 넘어가게 된다. 그런데 위 예시에는 에러를 처리해줄 핸들러가 없기 때문에 에러가 갇혀버린다.
아래 예시에서 .catch가 트리거될까?
new Promise(function (resolve, reject) {
setTimeout(() => {
throw new Error("에러 발생!");
}, 1000);
}).catch(alert);
.catch는 트리거 되지 않는다.
new Promise(function(resolve, reject) {
setTimeout(() => {
throw new Error("에러 발생!");
}, 1000);
}).catch(alert);
'암시적 try..catch'가 함수 코드를 감싸고 있으므로 모든 동기적 에러는 '암시적 try..catch'에서 처리된다. 하지만 여기에서 에러는 executor(실행자, 실행 함수)가 실행되는 동안이 아니라 나중에 발생한다.
복수의 URL에 동시에 요청을 보내고, 모두 완료된 후에 처리할 때 Promise.all을 사용할 수 있다.
let promise = Promise.all([...promises]);
Promise.all은 요소 전체가 프라미스인 배열(이터러블 객체이지만, 대개는 배열)을 받고 새로운 프라미스를 반환한다.
배열 안 프라미스가 모두 처리되면 새로운 프라미스가 이행되는데, 배열 안 프라미스의 결괏값을 담은 배열이 새로운 프라미스의 result가 된다.
Promise.all([
new Promise(resolve => setTimeout(() => resolve(1), 3000),
new Promise(resolve => setTimeout(() => resolve(2), 2000),
new Promise(resolve => setTimeout(() => resolve(3), 1000),
]).then(alert); // [1, 2, 3]
배열 result의 요소 순서는 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도 프라미스를 취소하지 않는다. AbortController를 사용하면 프라미스 취소가 가능하긴 하지만, 프라미스API는 아니다.
💡 이터러블 객체가 아닌 일반값도 Promise.all(iterable)에 넘길 수 있다.
Promise.all(...)은 대개 프라미스가 요소인 이터러블 객체를 받지만, 요소가 프라미스가 아닌 객체일 경우엔 요소 그대로 결과 배열에 전달된다.
Promise.all([
new Promise((resolve, reject) => {
setTimeout(() => resolve(1), 1000)
}),
2,
3
]).then(alert); // 1, 2, 3
Promise.allSettled는 모든 프라미스가 처리될 때까지 기다린다.
{status: "fulflled", value: result}{status: "rejected", reason: error}여러 요청 중 하나가 실패해도 다른 요청의 경과는 여전히 필요한 경우 사용할 수 있다.
Promise.allSettled(urls.map(url => fetch(url)))
.then(results => { //(*)
results.forEach((result, num) => {
if(result.status === "fulfilled") {
alert(`${url[num]}: ${result.value.status}`);
}
if(result.status === "rejected") {
alert(`${url[num]}: ${result.reason}`);
}
});
});
(*)로 표시한 줄의 results
[
{status: 'fulfilled', value: ...응답...},
{status: 'fulfilled', value: ...응답...},
{status: 'rejected', reason: ...에러 객체...}
]
Promise.allSettled를 사용하면 각 프라미스의 상태와 값 또는 에러를 받을 수 있다.
Promise.race는 Promise.all과 비슷하다. 다만 가장 먼저 처리되는 프라미스의 결과 혹은 에러를 반환한다.
let promise = Promise.race([
new Promise((resolve, reject) => setTimeout(() => resolve(1), 1000),
new Promise((resolve, reject) => setTimeout(() => rejected(new Error("에러 발생!")), 2000),
new Promise((resolve, reject) => setTimeout(() => resolve(3), 3000),
]).then(alert) // 1
첫 번째 프라미스가 가장 빨리 처리상태가 되기 때문에 첫 번째 프라미스의 결과가 result값이 된다. 이렇게 Promise.race를 사용하면 '경주(race)의 승자'가 나타난 순간 다른 프라미스의 결과 또는 에러는 무시된다.
[출처]
https://ko.javascript.info/promise-basics
https://ko.javascript.info/promise-chaining
https://ko.javascript.info/promise-error-handling
https://ko.javascript.info/promise-api