모던자바스크립트, 프로미스 객체

Edwin·2023년 3월 6일
0
post-thumbnail
  • 본 포스트은 모던자바스크립트 Deep Dive 45장을 기반으로 정리했습니다.
  • 본 포스티는 다크모드에 최적화 되어 있습니다.

프로미스 객체

JS는 싱글스레드이기에 동기적 프로그램을 지원한다. 그런데 비동기적 처리를 요청할 수는 없을까? 전통적인 패턴에서 이를 처리하기 위해서 도입된 방법이 콜백함수였지만, 해당 패턴은 가독성이 나쁘고 에러가 발생되었을 때 수정에 용이하지 못하다는 단점을 가지고 있었다.

1) ES6 - Promise 객체의 도입

ES6(2015년)을 발표하며 Promise라는 새로운 개념이 도입되며, 이전에 어렵게 작성되었던 비동기 프로그램패턴의 개선이 이뤄졌다.

프로미스의 생성

Promise 생성자 함수는 new 연산자와 함께, 새로운 Promise 객체를 생성한다. 해당 생성자 함수는 비동기 처리를 위한 콜백함수를 인수로 전달받는데, resolvereject 함수를 받는다.

const promise = new Promise((resolve, reject)=>{
	if(/* 비동기 처리 성공시 */) {
    	resolve('result')   
    } else { /* 비동기 처리 실패시 */
    	reject('failure reason')
    }
})

기v 같다.

2) 비동기 처리에 대한 상태정보

Promise는 비동기 처리에 대한 상태정보를 갖는데, 아래와 같다.

  • pending : 비동기 처리가 아직 수행되지 않은 상태
  • settled : 비동기 처리가 수행된 상태
    1. fulfilled : 수행되었고, 성공했을 때 -> resolve 함수 호출 -> fulfilled 상태반영
    2. rejected : 수행되었고, 실패했을 때 -> reject 함수 호출 -> rejected 상태반영

3) 프로미스의 후속처리 메서드

비동기 처리의 수행 결과에 따라 상태가 변경되면, 프로미스는 후속 처리를 해야 한다. 즉 fulfilled 상태에서는 그 결과를 가지고 무언가를 해야 할지, rejected 상태에서는 그 결과를 가지고 무언가를 해야 할지에 대한 논의이다.

  • Promise.prototype.then : 2개의 콜백함수를 인수로 받는데, 위에서 다루었던 resolvereject 함수이다.
// 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 메서드는 언제나 프로미스를 반영하며, 만약 콜백함수가 프로미스가 아닌 값을 반환하면, 인수로 받은 두 함수를 생성하여 반환한다.

  • Promise.prototype.catch : 한 개의 콜백 함수(rejected)만 인수로 전달받는다.
// rejected
new Promise((_, reject) => reject(new Error('rejected')))
	.catch(e=> console.error(e)) // Error:rejected
  • Promise.prototype.finally : 콜백 함수의 성공과 실패와 상관없이 무조건 단회적으로 한 개의 콜백 함수를 인수로 전달받는다. 즉 프로미스의 상태와 상관없이 공통적으로 수행해야 되는 처리 내용이 있을 때 사용되는 메서드이다.
new Promise(() => {})
	.finally(() => console.log('finally')) // finally

4) MDN의 설명을 보자

MDN문서는 쉽게 설명이 되어 있는데, Promise는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타내는 객체이다.

기존의 비동기처리에서 사용했었던 콜백함수는 값을 콜백으로 전달하고 처리된 결과를 반환받았다면, 프로미스는 콜백을 첨부하는 방식으로 일을 처리한다는 것이 MDN의 설명이다.

프로미스가 비동기처리를 처리하는 방식

첫째, 고전적인 방식과는 달리, 프로미스는 첨부한 콜백에 대해서 현재 실행중인 콜 스택이 완료되기 이전에 절대 이벤트 루프가 호출되지 않는다고 하였다.
둘째, 비동기 작업의 두 개의 결과(성공, 실패)가 진행된 이후에, then()을 이용하여 추가한 콜백의 경우에 이벤트 루프에 따라 호출되며, 여러번 사용하여 여러개의 콜백을 추가할 수 있다는 장점이 있다.

가장 뛰어난 프로미스의 장점은 chaining이다. (일단은 간추림)

이는 두 개 이상의 비동기 작업을 순차적으로 실행해야 하는 상황을 보게 된다.

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);

5) 프로미스 객체를 실제로 살펴보자.

아래의 코드는 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()의 결과가 성공하면 "와! 성공!"이 기록될 것이다.

프로미스에 대한 부분은 조금 더 공부가 필요한 것 같은데, 모던 자바스크립트는 이해가 현재적으로는 어렵다. 드림코딩의 강의를 참고하여 비동기 프로그래밍에 대해서 다뤄보자.

profile
신학전공자의 개발자 도전기!!

0개의 댓글