티스토리 뷰
11.2 Promise
- 제작 코드(producing code)'는 원격에서 스크립트를 불러오는 것 같은 시간이 걸리는 일을 한다.
- '소비 코드(consuming code)'는 '제작 코드’의 결과를 기다렸다가 이를 소비한다. 소비 주체(함수)는 여럿이 될 수 있다.
- 프라미스(promise) 는 '제작 코드’와 '소비 코드’를 연결해 주는 특별한 자바스크립트 객체다. '프라미스’는 시간이 얼마나 걸리든 상관없이 약속한 결과를 만들어 내는 '제작 코드’가 준비되었을 때, 모든 소비 코드가 결과를 사용할 수 있도록 해준다.
promise는 아래 예시처럼 만들 수 있다. 전달되는 함수는 executor 함수라고 부른다.
let promise = new Promise(function(resolve, reject) {
// executor (제작 코드, '가수')
});
executor는 promise가 만들어질 때 자동으로 실행되고, 결과를 최종적으로 만드는 제작코드를 포함한다. resolve, reject는 자체적으로 제공되는 콜백이다.
executor에서는 결과를 언제 얻든지 간에 상황에 따라 인수로 넘겨준 콜백 중 하나를 반드시 호출해야 한다.
- resolve(value) - 성공적으로 끝난 경우 그 결과를 나타내는 value와 함께 호출
- reject(error) - 에러 발생 시 에러 객체를 나타내는 error와 함께 호출
new Promise 생성자가 반환하는 객체는 아래 속성을 가진다.
- state — 처음엔 "pending"(보류)이었다 resolve가 호출되면 "fulfilled", reject가 호출되면 "rejected"로 변합니다.
- result — 처음엔 undefined이었다 resolve(value)가 호출되면 value로, reject(error)가 호출되면 error로 변합니다.
결국 executor의 결과에 따라 promise 객체의 상태가 변화되는 것이다.
성공과 실패 예시
// 성공
let promise = new Promise(function(resolve, reject) {
// 프라미스가 만들어지면 executor 함수는 자동으로 실행됩니다.
// 1초 뒤에 일이 성공적으로 끝났다는 신호가 전달되면서 result는 '완료'가 됩니다.
setTimeout(() => resolve("완료"), 1000);
});
// -----------
// 실패
let promise = new Promise(function(resolve, reject) {
// 1초 뒤에 에러와 함께 실행이 종료되었다는 신호를 보냅니다.
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
프라미스는 성공 아니면 실패만 한다. 이미 처리된 프라미스에 resolve와 reject를 호출하면 무시된다.
let promise = new Promise(function(resolve, reject) {
resolve("완료");
reject(new Error("…")); // 무시됨
setTimeout(() => resolve("…")); // 무시됨
});
state, result 속성은 내부 속성이라서 개발자가 직접 접근하기 어렵다.
Consumers: then, catch, finally
세가지 함수를 사용하여 소비 동작을 수행할 수 있다.
then
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: 에러 발생!"을 출력
);
catch
에러가 발생한 경우만 다루고 싶을 때, catch를 사용한다.
let promise = new Promise((resolve, reject) => {
setTimeout(() => reject(new Error("에러 발생!")), 1000);
});
// .catch(f)는 promise.then(null, f)과 동일하게 작동합니다
promise.catch(alert); // 1초 뒤 "Error: 에러 발생!" 출력
then(null, errorFunction)로도 가능하다. catch(errorFunction)과 동일하게 작동한다.
finally
try...catch와 같이 finally가 존재한다.
new Promise((resolve, reject) => {
/* 시간이 걸리는 어떤 일을 수행하고, 그 후 resolve, reject를 호출함 */
})
// 성공·실패 여부와 상관없이 프라미스가 처리되면 실행됨
.finally(() => 로딩 인디케이터 중지)
.then(result => result와 err 보여줌 => error 보여줌)
finally는 프라미스 이행, 거부 여부를 모른다. 그저 보편적인 동작만 수행하도록 설계되어 있다. 또한, 자동으로 바로 다음 핸들러로 결과와 에러를 전달한다.
new Promise((resolve, reject) => {
setTimeout(() => resolve("결과"), 2000)
})
.finally(() => alert("프라미스가 준비되었습니다."))
.then(result => alert(result)); // <-- .then에서 result를 다룰 수 있음
finally인데 마지막에 사용되지 않는게 의아하다. 이는 결과를 처리하기 위해 만들어진게 아니기 때문이다. 체이닝에서 중요하게 다뤄질 것 같다.
Example: loadScript
function loadScript(src) {
return new Promise(function(resolve, reject) {
let script = document.createElement('script');
script.src = src;
script.onload = () => resolve(script);
script.onerror = () => reject(new Error(`${src}를 불러오는 도중에 에러가 발생함`));
document.head.append(script);
});
}
// ---------------
let promise = loadScript("https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.js");
promise.then(
script => alert(`${script.src}을 불러왔습니다!`),
error => alert(`Error: ${error.message}`)
);
promise.then(script => alert('또다른 핸들러...'));
'스터디 > JavaScript' 카테고리의 다른 글
모던 자바스크립트 튜토리얼 part1 - 11.4 (0) | 2024.05.27 |
---|---|
모던 자바스크립트 튜토리얼 part1 - 11.3 (0) | 2024.05.27 |
모던 자바스크립트 튜토리얼 part1 - 11.1 (0) | 2024.05.27 |
모던 자바스크립트 튜토리얼 part1 - 10.2 (0) | 2024.05.27 |
모던 자바스크립트 튜토리얼 part1 - 10.1 (0) | 2024.05.27 |