자바스크립트에서 함수를 호출했을 때 함수가 시작되고 끝나는 동안에도 프로그램은 계속 진행되어야 할 때 가 있다. promise를 사용하면 비동기적인 상황에서 코드를 명확하게 표현하고 실행하도록 만들 수 있다.
Ajax Web API요청, 파일 읽기 등 서버에서 받아 올 때
암호화/복호화, 작업예약 등 시간이 걸리는 작업에서 비동기 처리가 필요하다.
ES6부터 JavaScript의 표준 내장 객체로 추가
ES6를 지원하는 브라우저나 Node.js에서 전역에 있는 Promise 확인 가능
console.log(Promise);
[Function: Promise]
생성자를 통해서 프로미스 객체를 만들 수 있다.
생성자의 인자로 executor 함수 이용
new Promise(/*executor*/);
executor 함수는 resolve와 reject를 인자로 가진다.
(resolve, reject) => {...}
resolve와 reject는 함수이다.
resolve(), reject()
new Promise(/*executor*/(resolve, reject) => {});
생성자를 통해서 프로미스 객체를 만드는 순간 pending(대기) 상태라고 한다.
new Promise((resolve, reject)=>{}); // pending
executor 함수 인자 중 하나인 resolve 함수를 실행하면, fulfilled(이행) 상태가 된다.
new Promise((resolve, reject)=>{
// pending 상태
//..
resolve(); //fulfilled 상태
});
executor 함수 인자 중 하나인 reject 함수를 실행하면, rejected(거부) 상태가 된다.
new Promise((resolve, reject)=>{
// pending 상태
//..
resolve(); //fulfilled 상태
reject(); // rejected 상태
});
p라는 프로미스 객체는 1000ms 후에 fulfilled 됩니다.
new Promise((resolve, reject) => {
// pending
setTimeout(() => {
resolve(); //fulfulled
}, 1000);
})
p라는 프로미스 객체가 fulfilled되는 시점에 p.then안에 설정한 callback 함수가 실행됩니다.
const p = new Promise((resolve, reject) => {
// pending
setTimeout(() => {
resolve(); //fulfulled
}, 1000);
});
p.then(/* callback */);
then은 fulfilled가 끝나는 시점에 시작된다.
const p = new Promise((resolve, reject) => {
// pending
setTimeout(() => {
resolve(); //fulfulled
}, 1000);
});
p.then(() => {
console.log('1000ms 후에 fulfilled 됩니다');
});
1000ms 후에 fulfilled 됩니다
1초 후에 console.log 내용이 나온다.
then을 설정하는 시점을 정확히 하고, 함수의 실행과 동시에 프로미스 객체를 만들면서 pending이 시작하도록 하기 위해 프로미스 객체를 생성하면서 리턴하는 함수 (p)를 만들어 함수 (p) 실행과 동시에 then을 설정합니다.
function p() {
return new Promise((resolve, reject) => {
// pending
setTimeout(() => {
resolve(); //fulfulled
}, 1000);
});
}
p().then(() => {
console.log('1000ms 후에 fulfilled 됩니다.');
})
1000ms 후에 fulfilled 됩니다
위와 비슷하지만 대규모 작업에서 좀 더 빠르다.
프로미스 객체가 rejected 되는 시점에 p.catch 안에 설정한 callback 함수가 실행됩니다.
function p() {
return new Promise((resolve, reject) => {
// pending
setTimeout(() => {
reject(); //rejected
}, 1000);
});
}
p().then(() => {
console.log('1000ms 후에 fulfilled 됩니다.');
}).catch(() => {
console.log('1000ms 후에 rejected 됩니다.');
});
1000ms 후에 rejected 됩니다.
1초 후에 catch 실행
executor의 resolve 함수를 실행할 때 인자를 넣어 실행하면, then의 callback 함수의 인자로 받을 수 있습니다.
resolve('hello');
then((message) => {...})
function p() {
return new Promise((resolve, reject) => {
// pending
setTimeout(() => {
resolve('hello'); //fulfulled
}, 1000);
});
}
p().then((message) => {
console.log('1000ms 후에 fulfilled 됩니다.', message);
}).catch(() => {
console.log('1000ms 후에 rejected 됩니다.');
});
1000ms 후에 fulfilled 됩니다. hello
이렇게 데이터를 넘기는 것은 자주 쓰는 방법이다.
executor의 reject 함수를 실행할 때 인자를 넣어 실행하면, catch의 callback 함수의 인자로 받을 수 있다.
reject('error');
then((reason) => {...})
function p() {
return new Promise((resolve, reject) => {
// pending
setTimeout(() => {
reject('error'); // rejected
}, 1000);
});
}
p().then((message) => {
console.log('1000ms 후에 fulfilled 됩니다.', message);
}).catch((reason) => {
console.log('1000ms 후에 rejected 됩니다.', reason);
});
1000ms 후에 rejected 됩니다. error
보통 reject 함수를 실행하며 rejected 되는 이유를 넘기는데, 표준 내장 객체인 Error의 생성자를 이용하여 Error 객체를 만들 수 있다.
function p() {
return new Promise((resolve, reject) => {
// pending
setTimeout(() => {
reject(new Error('bad'));
}, 1000);
});
}
p().then((message) => {
console.log('1000ms 후에 fulfilled 됩니다.', message);
}).catch((error) => {
console.log('1000ms 후에 rejected 됩니다.', error);
});
1000ms 후에 rejected 됩니다. Error: bad at Timeout._onTimeout (d:\project\javascript\new\promise1.js:32:20) at listOnTimeout (internal/timers.js:557:17) at processTimers (internal/timers.js:500:7)
fulfilled 되거나 rejected 된 후에 최종적으로 실행할 것이 있다면, .finally()를 설정하고, 함수를 인자로 넣습니다.
function p() {
return new Promise((resolve, reject) => {
// pending
setTimeout(() => {
reject(new Error('bad'));
}, 1000);
});
}
p()
.then((message) => {
console.log('1000ms 후에 fulfilled 됩니다.', message);
})
.catch((error) => {
console.log('1000ms 후에 rejected 됩니다.', error);
})
.finally(() => {
console.log('end');
});
1000ms 후에 rejected 됩니다. Error: bad at Timeout._onTimeout (d:\project\javascript\new\promise1.js:32:20) at listOnTimeout (internal/timers.js:557:17) at processTimers (internal/timers.js:500:7) end
보통 비동기 작업을 할 때, callback 함수를 인자로 넣어 로직이 끝나면 callback 함수를 호출합니다. 이런 경우 함수가 아래로 진행되지 않고, callback 함수 안으로 진행됩니다.
function c(callback) {
setTimeout(() => {
callback();
},1000);
}
c(() => {
console.log('1000ms 후에 callback 함수가 실행됩니다.');
});
//들여쓰기로 코드가 callback 함수 안으로 진행(callback hell)
c(() => {
c(() => {
c(() => {
console.log('3000ms 후에 callback 함수가 실행됩니다.');
});
});
});
1000ms 후에 callback 함수가 실행됩니다.
3000ms 후에 callback 함수가 실행됩니다.
then 함수에서 다시 프로미스 객체를 리턴하는 방법을 통해 체이닝하면, 비동기 작업을 순차적으로 아래로 표현할 수 있습니다.
function p() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
})
}
p().then(() => {
return p(); // 다시 p함수 실행 ,1초 뒤
})
.then(() => p()) // 위와 동일, 2초 뒤
.then(p) // p함수 실행, 3초 뒤
.then(() => { // 4초 뒤
console.log('4000ms 후에 fulfilled 됩니다.')
})
4000ms 후에 fulfilled 됩니다.
/* value */
);value가 프로미스 객체인지 아닌지 알 수 없는 경우, 사용하면 연결된 then 메서드를 실행합니다.
Promise.resolve(new Promise((resolve, reject) => {
setTimeout(() => {
resolve('foo');
}, 1000);
})).then((data) => {
console.log('프로미스 객체인 경우, resolve 된 결과를 받아 then 이 실행 됩니다.', data)
})
Promise.resolve('bar').then(data => {
console.log('then 메서드가 없는 경우, fulfilled 됩니다', data);
})
then 메서드가 없는 경우, fulfilled 됩니다. bar
프로미스 객체인 경우, resolve 된 결과를 받아 then 이 실행 됩니다. foo
어떤 객체가 객체인지 아닌지 알 수 없는 경우 Promise.resolve로 한번 실행해서 넘기면 resolve로 되거나 아니면 값이 바로 넘어간다
/* value */)
;Promise.reject 를 사용하면, catch 로 연결된 rejected 상태로 변경됩니다.
Promise.reject(new Error('reason')).then(error => {
}).catch(error => {
console.log(error);
})
Error: reason at Object.<anonymous> (d:\project\javascript\new\promise1.js:97:16) at Module._compile (internal/modules/cjs/loader.js:1085:14) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10) at Module.load (internal/modules/cjs/loader.js:950:32) at Function.Module._load (internal/modules/cjs/loader.js:790:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:76:12) at internal/main/run_main_module.js:17:47
프로미스 객체 여러개를 생성하여, 배열로 만들어 인자로 넣고 Promise.all 을 실행하면, 배열의 모든 프로미스 객체들이 fulfilled 되었을 때, then의 함수가 실행됩니다. then의 함수의 인자로 프로미스 객체들의 resolve 인자값을 배열로 돌려줍니다.
function p(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, ms);
})
}
Promise.all([p(1000), p(2000), p(3000)]).then(() => {
console.log('모두 fulfilled 된 이후에 실행됩니다.');
})
모두 fulfilled 된 이후에 실행됩니다.
3초 후에 실행됨
function p(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(ms);
}, ms);
})
}
Promise.all([p(1000), p(2000), p(3000)]).then((messages) => {
console.log('모두 fulfilled 된 이후에 실행됩니다.', messages);
})
모두 fulfilled 된 이후에 실행됩니다. [ 1000, 2000, 3000 ]
3초 후에 실행 됨
프로미스 객체 여러개를 생성하여, 배열로 만들어 인자로 넣고 Promise.race를 실행하면, 배열의 모든 프로미스 객체들 중 가장 먼저 fulfilled 된 것으로, then의 함수가 실행됩니다. then의 함수 인자로 가장 먼저 fulfilled된 프로미스 객체의 resolve 인자값을 돌려줍니다.
function p(ms) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(ms);
}, ms);
})
}
Promise.race([p(1000), p(2000), p(3000)]).then((message) => {
console.log('가장 빠른 하나가 fulfilled 된 이후에 실행됩니다.', message);
})
가장 빠른 하나가 fulfilled 된 이후에 실행됩니다. 1000