Promise
객체 생성하기promise
객체를 직접 생성하기 위한 기본 코드는 다음과 같다.
const p = new Promise((resolve, reject) => {
});
위 코드에서, promise
객체 내 작성된 함수는 객체가 생성될 때 자동으로 실행되는 executor 함수이다.
resolve
파라미터에는 생성될 promise
객체를 fulfilled
상태로 만들 수 있는 함수가 연결되며,
reject
파라미터에는 생성될 promise
객체를 rejected
상태로 만들 수 있는 함수가 연결된다.
아래 예시 코드의 경우, p
라는 promise
객체가 2초 후에 fulfilled
상태가 되며, 그 때 resolve
함수 안에 넣은 success
라는 문자열이 작업 성공 결과가 되고, 결과적으로 success
라는 문자열이 출력된다.
const p = new Promise((resolve, reject) => {
setTimeout(() => {resolve('success');}, 2000);
});
p.then((result) => {console.log(result);});
반면, 아래 예시 코드의 경우, p
라는 promise
객체가 2초 후에 rejected
상태가 되며, 그 때 reject
함수 안에 넣은 fail
이라는 문자열이 작업 실패 결과가 되고, 결과적으로 fail
이라는 메세지를 가진 에러 객체가 출력된다.
const p = new Promise((resolve, reject) => {
setTimeout(() => { reject(new Error('fail')); }, 2000);
});
p.catch((error) => { console.log(error); })
지금까지 설명했던 것과 같이, pending
상태에 있다가 다른 상태로 변하는 promise
객체를 만드는 것이 아니라, 처음부터 fulfilled
상태이거나 rejected
상태인 promise
객체를 만드는 방법도 존재한다.
fulfilled
상태promise
객체의 resolve
method를 사용하면, 바로 fulfilled
상태의 promise
객체를 생성할 수 있다.
const p = Promise.resolve('success');
rejected
상태promise
객체의 reject
method를 사용하면, 바로 rejected
상태의 promise
객체를 생성할 수 있다.
const p = Promise.reject(new Error('fail'));
이렇게 promise
객체를 직접 만드는 코드는 promisify라고 하는 작업을 할 때 주로 작성하게 된다.
아래 코드의 실행 결과를 예측해보자.
function wait(text, milliseconds) {
setTimeout(() => text, milliseconds);
}
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.text())
.then((result) => wait(`${result} by Codeit`, 2000))
.then((result) => { console.log(result); });
코드를 실행하면, 2초 뒤에 response 내용과 by Codeit
이 붙어서 출력이 되어야 할 것 같지만, 실제로는 undefined
가 출력된다.
그 이유는, wait
함수는 setTimeout
함수를 실행하기만 할 뿐 아무것도 리턴하지 않기 때문이다.
(setTimeout
함수의 test
와 wait
이 함수의 리턴값이 아님을 주의해야 한다.)
이렇게, Promise Chaining 안에서 setTimeout
과 같은 비동기 실행 함수를 바로 사용하면, 나중에 실행되는 부분의 리턴값(text
)를 Promise Chain에서 사용할 수 없게 되는데,
이를 해결하기 위한 방법이 바로 promise
객체를 직접 생성하는 것이다.
위 코드를 아래와 같이 수정해보자.
function wait(text, milliseconds) {
const p = new Promise((resolve, reject) => {
setTimeout(() => { resolve(text); }, 2000);
});
return p;
}
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => response.text())
.then((result) => wait(`${result} by Codeit`, 2000))
.then((result) => { console.log(result); });
wait
함수 내에서 p
라는 promise
객체를 직접 생성하였고, executor 함수 안에서 setTimeout
함수를 호출하고 있다.
이후, setTimeout
함수 안의 콜백에서 resolve
함수를 호출할 때 파라미터로 text
를 받고 있다.
이 경우에는, 앞선 경우와는 다르게, p
라는 promise
객체가 2초 후에 fulfilled
상태가 될 것이고, 그 작업 성공 결과가 text
라는 파라미터의 값이 되어, 결과적으로 wait
함수가 p
라는 promise
객체를 리턴할 수 있게 된다.
이와 같이, 전통적인 형식의 비동기 실행 함수를 promise
객체로 감싸서 그 promise
객체를 리턴하는 형식으로 만드는 작업을 Promisify
라고 한다.
단, 모든 비동기 실행 함수를 Promisify할 수 있는 것은 아니며, 기존의 비동기 실행 함수들 중, 그 콜백을 한 번만 실행하는 것들, 예를 들면, setTimeout
, readFile
등과 같은 것들만 promisify를 할 수 있다.
그 이유는, promise
객체는 한 번 pending
상태에서 fulfilled
또는 rejected
상태가 되면, 그 뒤로는 그 상태와 결과가 바뀌지 않기 때문이다.
때문에 아래와 같이 코드를 작성하게 되면, 처음에 1
이 출력된 이후에, 그 다음 count
값들은 출력되지 않는다.
const box = document.getElementById('test');
let count = 0;
function addEventListener_promisified(obj, eventName) { // 이런 Promisify는 하지 마세요
const p = new Promise((resolve, reject) => {
obj.addEventListener(eventName, () => { // addEventListener 메소드
count += 1;
resolve(count);
});
});
return p;
}
addEventListener_promisified(box, 'click').then((eventCount) => { console.log(eventCount); });