Promise 기반의 API를 사용해서 Promise 객체를 얻는 경우가 대부분이지만, Promise 객체를 반환하는 API를 직접 구현해야 할 때도 있다.
그에 대해 알아보자.
이 예제에서는 alarm()
이라는 Promise 기반의 API를 구현한다.
깨울 사람의 이름과 지금으로 부터 몇초 후인지를 결정하는 시간이 매개변수로 사용된다.
시간이 다 지나면 깨울 사람의 이름을 포함하여 "Wake up!" 메시지를 보낼 것이다.
alarm()
함수를 만들기 위해 setTimeout()
API를 사용할 것이다.
setTimeout()
API는 콜백 함수와 delay(밀리초 단위)를 전달 받는다.
setTimeout()
이 호출될 때, 전달 받은 delay로 설정된 타이머가 흐르기 시작하고 시간이 만료되면 전달 받은 콜백 함수를 호출한다.
<button id="set-alarm">Set alarm</button>
<div id="output"></div>
const output = document.querySelector('#output');
const button = document.querySelector('#set-alarm');
function setAlarm() {
setTimeout(() => {
output.textContent = 'Wake up!';
}, 1000);
}
button.addEventListener('click', setAlarm);
alarm()
함수는 타이머가 만료되면 fulfilled Promise를 반환한다.
그리고 "Wake up!" 메시지를 then()
핸들러로 전달한다.
alarm()
의 delay 매개변수에 음수가 전달되면 rejected Promise를 반환한다.
여기서 핵심은 Promise() constructor이다.
Promise() constructor는 매개변수로 하나의 함수를 전달받는다.
전달 받는 이 함수를 Executor(실행자)라고 부른다.
즉, 새 Promise를 만들려면 executor을 구현해서 constructor를 호출해야 한다.
Executor는 두 개의 매개변수를 사용하는데 일반적으로 resolve
와 reject
라고 부른다.
Executor 내부에서 비동기 함수를 호출한다.
비동기 함수가 성공하면 resolve()
를 호출하고 실패하면 reject()
를 호출한다.
executor에서 오류가 발생하면 자동으로
reject()
가 호출되므로 명시적으로 호출하지 않아도 된다.
resolve()
와 reject()
는 하나의 argument를 가지는데 모든 유형의 값을 전달받을 수 있다.
function alarm(person, delay) {
return new Promise((resolve, reject) => {
if (delay < 0) {
throw new Error('Alarm delay must not be negative');
}
setTimeout(() => {
resolve(`Wake up, ${person}!`);
}, delay);
});
}
delay
가 음수인지 아닌지 확인하고 음수인 경우 오류를 발생시킨다.
오류가 발생되면 자동으로 Error객체를 전달받은 reject()
가 호출된다.
setTimeout()
함수에 콜백함수와 delay를 전달하여 호출한다.
콜백 함수는 타이머가 만료되면 호출되고, 콜백 함수내부에서 "Wake up!" 메시지를 resolve()
함수에 전달하여 호출한다.
완성된 코드는 다음과 같다.
const name = document.querySelector('#name');
const delay = document.querySelector('#delay');
const button = document.querySelector('#set-alarm');
const output = document.querySelector('#output');
function alarm(person, delay) {
return new Promise((resolve, reject) => {
if (delay < 0) {
throw new Error('Alarm delay must not be negative');
}
setTimeout(() => {
resolve(`Wake up, ${person}!`);
}, delay);
});
}
button.addEventListener('click', () => {
alarm(name.value, delay.value)
.then((message) => output.textContent = message)
.catch((error) => output.textContent = `Couldn't set alarm: ${error}`);
});
alarm()
함수가 Promise를 반환하기 때문에 Promise chaining, Promise.all()
, async
/ await
와 같은 Promise 기반 코드를 사용할 수 있다.
const name = document.querySelector('#name');
const delay = document.querySelector('#delay');
const button = document.querySelector('#set-alarm');
const output = document.querySelector('#output');
function alarm(person, delay) {
return new Promise((resolve, reject) => {
if (delay < 0) {
throw new Error('Alarm delay must not be negative');
}
setTimeout(() => {
resolve(`Wake up, ${person}!`);
}, delay);
});
}
button.addEventListener('click', async () => {
try {
const message = await alarm(name.value, delay.value);
output.textContent = message;
}
catch (error) {
output.textContent = `Couldn't set alarm: ${error}`;
}
});
[참고] : MDN