본문 바로가기
프로그래밍/JavaScript

Promise란??

by HP_Factory 2021. 1. 20.

JavaScript의 세계에서는 거의 대부분의 작업들이 비동기로 이루어집니다. 어떤 작업을 요청하면서 콜백 함수를 등록하면, 작업이 수행되고 나서 결과를 나중에 콜백 함수를 통해 알려주는 식입니다. 실제 비동기 작업이 아니더라도 JavaScript의 세계에서는 결과를 콜백으로 알려주는 패턴이 매우 흔하게 사용되고 있습니다.

 

초기의 JavaScript의 경우 버튼이 눌렸을 때(이벤트 발생) 특정 작업을 수행(콜백 함수 호출)하는 정도의 수준이었기 때문에 복잡도가 높지 않았지만 최근에는 프론트엔드의 규모가 상당히 커져서 JavaScript로 작성하는 코드를 단순하게 바라볼 수준은 넘어선지 오래입니다.

 

이렇게 복잡도가 높아지는 상황에서 특히 어려워지는 케이스는 콜백이 중첩되는 경우입니다. 하나의 작업을 콜백으로 결과를 받은 뒤 순차적으로 다음 작업을 진행하고자 할 때 이러한 콜백 중첩, 이른바 콜백 지옥을 만나게 되는 것입니다.

 

콜백 지옥...

 

 

 

Promise??

이런 상황을 극복하기 위해 오래전부터 Promise라는 패턴이 제안되어 왔습니다. jQuery에서는 Deferred라는 이름으로 완전하진 않지만 Promise 패턴이 사용되었고, 그 외에도 Q, Vow, Bluebird 등 다양한 라이브러리를 통해 Primise 패턴을 구현해서 콜백 중첩으로 인한 어려움들을 해소해왔습니다.

 

Promise 패턴을 사용하여 코드를 잘 짜면 비동기 작업들을 순차적으로 진행하거나, 병렬로 진행하는 등의 컨트럴이 보다 수월해지고 코드의 가독성이 좋아집니다.

 

내부적으로 예외처리에 대한 구조가 탄탄하기 때문에 오류가 발생했을 때 오류 처리 등에 대해 보다 가시적으로 관리해줄 수 있는 장점이 있습니다.

 

 

 

Promise 기초

실행 결과는 콘솔로그로 "해결 완료"가 뜨는 것을 볼 수 있습니다.

 

위의 코드는 크게 Promise 선언과 실행 두 부분으로 나눌 수 있습니다.

 

 

 

Promise 선언부

Promise는 말 그대로 "약속"입니다. "지금은 없으니까 이따가 줄게~" 라는 약속입니다. 더 정확히는 "지금은 없는데 이상 없으면 이따가 주고 없으면 알려줄게~"라는 약속입니다.

 

 

따라서 Promise는 다음 중 하나의 상태(State)가 될 것입니다.

 

Promise State
Promise 선언부(비동기 작업 시뮬레이션을 위한 setTimeout 함수 사용)

위의 Promise 선언부를 보면, 나중에 Promise 객체를 생성하기 위해 Promise 객체를 리턴하도록 함수로 감싸고 있습니다.

 

Promise 객체만 보면 파라메터로 익명함수를 담고 있고, 익명 함수는 resolve와 reject를 파라메터로 받고 있습니다.

 

일단 new Promise로 Promise가 생성되는 직후부터 resolve나 reject가 호출되기 전까지의 순간을 "pending 상태"라고 볼 수 있습니다.

 

이후 비동기 작업이 마친뒤 결과물을 약속대로 잘 줄 수 있다면 첫번째 파라메터로 주입되는 resolve 함수를 호출하고, 실패했다면 두번째 파라메터로 주입되는 reject 함수를 호출한다는 것이 promise의 주요 개념(!)입니다.

 

 

 

 

Promise 실행부

 

실행하는 부분은 더욱 심플합니다. _promise()를 호출하면 Promise 객체가 리턴됩니다. Promise 객체에는 정상적으로 비동기작업이 완료되었을 때 호출하는 then 이라는 API가 존재합니다. 위의 예제는 하나의(!) then API를 호출해서 비동기 작업이 완료되면 결과에 따라 성공 혹은 실패 메시지를 콘솔로그로 찍어주게 됩니다.

 

then API는 첫번째 파라메터에 성공시 호출할 함수를, 두번째 파라메터에 실패시 호출할 함수를 선언하면 Promise의 상태에 따라 수행하게 됩니다.

 

앞서 선언부에서 Promise 객체를 생성할 때 resolve와 reject 파라메터를 받았는데 그 파라메터가 실행부의 함수와 동일합니다.

 

 

 

Promise.catch API

만약 체이닝형태로 연결된 상태에서 비동기 작업이 중간에 에러가 나면 어떻게 처리해야할까요? 그때를 위해 존재하는 API가 Catch API입니다. .then(null, function(){}) 을 메서드 형태로 바꾼 거라고 생각해도 좋습니다.

 

Promise.catch

앞서_Promise 에서 만든 객체는 성공 혹은 실패시 JSON 객체가 아닌 String을 리턴하므로 JSON.parse에서 Error가 나게 됩니다. 따라서 다음 then으로 이동하지 못하고 catch에서 받게 됩니다. catch는 이와같이 promise가 연결되어 있을 때 발생하는 오류를 처리해주는 역할을 합니다.

 

*체이닝 형태 : _promise(true).then(JSON.parse).catch(function () { window.alert('JSON이 아닌 해결완료라는 String이 반환되었습니다.');}) 처럼 "."으로 체인처럼 연결된 형태를 말합니다.

 

 

 

Promise.all API

여러개의 비동기 작업들이 존재하고 이들이 모두 완료되었을 때 작업을 진행하고 싶다면, Promise.all API를 활용하면 됩니다.

Promise.all API

두번째 Promise 가 완료된 뒤, 시간이 흘러 첫번째 Promise가 완료되면 최종적으로 전체값을 보여줍니다.

 

 

 

 

new Promise

위에 Promise.all API에서는 return이 아닌 바로 new Promise로 객체를 생성하였습니다. new Promise를 return이 아닌 바로 new Promise를 할당하는 형태로 사용하는 것의 차이를 비교해보겠습니다.

 

위와 같이 선언할 경우 Promise 객체에 파라메터로 넘겨준 익명함수는 즉각 실행됩니다. 즉각 실행되므로 _promise.then(alert) 등의 형태로 사용할 수 있습니다.

 

이후 여러차례 _promise.then(alert)를 호출해도 이미 한번 수행이 되었기 때문에 계속해서 resolve 혹은 reject가 수행될 것입니다.

 

즉, 다음과 같은 형태로 사용이 가능한 것입니다.

바로 new Promise를 생성

여기서 한번 "Stuff worked!"가 나왔다면, 몇 번을 반복해서 수행해도 계속 "Stuff worked!"가 나오게 됩니다.

 

 

 

 

이번에는 앞서 Promise.all에 대한 예제를 Promise를 return 하는 형태로 바꿀경우 어떻게 변하는지 확인해보겠습니다.

위와 같이 Promise 객체를 return 하는 형태로 바꿀 경우 위에서처럼(위에 경우는 익명함수를 사용하고 있습니다.)

와 같은 형태로는 Promise.all API를 사용할 수 없습니다. Promise 객체가 아니기 떄문에 오류 메시지를 만나게 됩니다.

 

따라서 아래와 같이 실행해야 정상적으로 Promise.all API를 호출할 수 있습니다.

위의 예제는 객체(promise1, promise2)가 아닌, 함수로서(promise1(), promise2()) 파라메터로 넣어주고 있습니다.

 

 

 

결론

비동기 로직이 불가피한 자바스크립트 코딩에서 효율적으로 비동기 로직을 처리할 수 있는 Promise를 손에 익혀둔다면 좀 더 가독성있는 코드를 만드는데 도움이 될 수 있을 것입니다.

 

 

 

 

 

참조

programmingsummaries.tistory.com/325

web.dev/promises/

github.com/dfilatov/vow

 

'프로그래밍 > JavaScript' 카테고리의 다른 글

ECMA SCript?란? (ES란?)  (0) 2021.01.22

댓글