JS는 싱글스레드이기에 동기적 프로그램을 지원한다. 그런데 비동기적 처리를 요청할 수는 없을까? 전통적인 패턴에서 이를 처리하기 위해서 도입된 방법이 콜백함수였지만, 해당 패턴은 가독성이 나쁘고 에러가 발생되었을 때 수정에 용이하지 못하다는 단점을 가지고 있었다.
ES6(2015년)을 발표하며 Promise라는 새로운 개념이 도입되며, 이전에 어렵게 작성되었던 비동기 프로그램패턴의 개선이 이뤄졌다.
Promise 생성자 함수는 new 연산자와 함께, 새로운 Promise 객체를 생성한다. 해당 생성자 함수는 비동기 처리를 위한 콜백함수를 인수로 전달받는데, resolve와 reject 함수를 받는다.
const promise = new Promise((resolve, reject)=>{
if(/* 비동기 처리 성공시 */) {
resolve('result')
} else { /* 비동기 처리 실패시 */
reject('failure reason')
}
})
기v 같다.
Promise는 비동기 처리에 대한 상태정보를 갖는데, 아래와 같다.
비동기 처리의 수행 결과에 따라 상태가 변경되면, 프로미스는 후속 처리를 해야 한다. 즉 fulfilled 상태에서는 그 결과를 가지고 무언가를 해야 할지, rejected 상태에서는 그 결과를 가지고 무언가를 해야 할지에 대한 논의이다.
// fulfilled
new Promise(resolove('fulfilled'))
.then(v => console.log(v), e=> console.error(e)) // fulfilled
// rejected
new Promise((_, reject) => reject(new Error('rejected')))
.then(v => console.log(v), e=> console.error(e)) // Error:rejected
then 메서드는 언제나 프로미스를 반영하며, 만약 콜백함수가 프로미스가 아닌 값을 반환하면, 인수로 받은 두 함수를 생성하여 반환한다.
// rejected
new Promise((_, reject) => reject(new Error('rejected')))
.catch(e=> console.error(e)) // Error:rejected
new Promise(() => {})
.finally(() => console.log('finally')) // finally
MDN문서는 쉽게 설명이 되어 있는데, Promise는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타내는 객체이다.
기존의 비동기처리에서 사용했었던 콜백함수는 값을 콜백으로 전달하고 처리된 결과를 반환받았다면, 프로미스는 콜백을 첨부하는 방식으로 일을 처리한다는 것이 MDN의 설명이다.
첫째, 고전적인 방식과는 달리, 프로미스는 첨부한 콜백에 대해서 현재 실행중인 콜 스택이 완료되기 이전에 절대 이벤트 루프가 호출되지 않는다고 하였다.
둘째, 비동기 작업의 두 개의 결과(성공, 실패)가 진행된 이후에, then()을 이용하여 추가한 콜백의 경우에 이벤트 루프에 따라 호출되며, 여러번 사용하여 여러개의 콜백을 추가할 수 있다는 장점이 있다.
이는 두 개 이상의 비동기 작업을 순차적으로 실행해야 하는 상황을 보게 된다.
const promise = doSomething()
const promise2 = promise.then(successCallback, failureCallback)
// 또는
const promise2 = doSomething().then(successCallback, failureCallback)
프로미스 이전에 콜백함수를 통해서 비동기처리를 하기 위해서는 아래 같은 콜백지옥에 빠질 수밖에 없었다.
doSomething(function(result) {
// 함수선언 후 콜백함수 를 통해서 해당 내용을 처리해야 했다.
doSomethingElse(result, function(newResult) {
doThirdThing(newResult, function(finalResult) {
console.log('Got the final result: ' + finalResult);
}, failureCallback);
}, failureCallback);
}, failureCallback);
아래의 코드는 MDN의 예제를 가져온 것이다.
// 프로미스 객체의 선언은 아래와 같다.
// 01 비동기 작업이 성공한 경우 resolve()가 호출되고
// 02 비동기 작업이 실패한 경우 reject()가 호출된다.
constt firstPromise = new Promise((resolve, reject) => {})
// setTimeout()에서 2000(2초)는 서버에서 받아올 때를 가정해서 설정한 딜레이 시간이다.
constt firstPromise = new Promise((resolve, reject) => {
setTimeout(() =>{
resolve("성공!")
}, 2000)
})
// 아래의 then()은 위의 첫번째 프로미스 다음에 실행되는 프로미스 체이닝이다.
// succesMessage란 위의 firstPromise의 결과로 발생된 "성공!"이라는 문자열이다.
firstPromise.then((succesMessage) => {
console.log("와!" + succesMessage)
})
위에서 선언한 firstPromise.then()의 결과가 성공하면 "와! 성공!"이 기록될 것이다.
프로미스에 대한 부분은 조금 더 공부가 필요한 것 같은데, 모던 자바스크립트는 이해가 현재적으로는 어렵다. 드림코딩의 강의를 참고하여 비동기 프로그래밍에 대해서 다뤄보자.