콜백 지옥에서 살아남기 - 01. promise

zwundzwzig·2022년 9월 4일
0

[javascript]

목록 보기
7/13
post-thumbnail

비동기적인 요소 중 기본적인 방식으로 콜백 기반 callback-based 비동기 프로그래밍이 있다. 그러나 만약 콜백 함수에 콜백 함수를 계속 중첩하는, 이른바 콜백 지옥의 코드를 작성한다면, 문제에 놓일 것이다.

가독성이 떨어지기 때문에 문제 발생 시 해당 코드를 다시 봤을 때 디버깅하거나 유지보수하기 어려울 것이며, 다른 개발자와의 소통 역시 어려울 것이다.

그래서 express 프레임워크나 다른 모듈을 로딩할 때 promise, async, await 등의 문법을 사용한다고 생각한다. 보다 효율적인 비동기 방식으로 구현해야 하기 때문!!

그래서 그 개념에 대한 정리는 express를 사용하면서 필수적으로 수행해야 한다고 생각했다.

promise

promise 함수는 javascript 내장 object이다. 비동기를 효율적으로 처리하도록 도와주는 친구.

처음에 개념 자체가 잡히지 않아 유튜버 '코딩엘리'님께서 비유하신 '강의 사전 구독'이나 사이트 '모던 JavaScript 튜토리얼'에서 비유한 '가수 앨범 발매 사전 구독' 등의 추상적인 비유를 통해 스스로 개념을 잡아봤다.

promise는, 원격에서 스크립트를 불러오는 등의 시간이 걸리는 일 producer 이 얼마의 시간 후 실현될 준비가 됐을 때, 기다리던 모든 함수 consumer 가 결과를 사용하게 하는 객체로 개념을 잡고 학습을 이어갔다. 물론, promise는 현직에 근무하시는 개발자분들도 부담스러울 만큼 방대하고 복잡한 개념이기 때문에 기초적인 이해를 수반하기 위해 위의 방식으로 개념을 잡아봤다.

실행자 함수

promise에 전달되는 함수는 실행자 함수 executor이다. 이것은 위의 비유로 따지면, producer이다.

실행자 함수는 결과를 언제 얻든 상관없이 상황에 따라 인수로 넘겨준 콜백 중 하나를 즉각적으로 호출해야 하는데, 처리 성공 여부에 따라 resolve(value) or reject(error)를 호출한다.

한편, promise 객체는 stateresult라는 내부 프로퍼티를 갖는다. 다음 그림은 지금까지 배운 promise 객체의 상황에 따른 state, result 이다.

소비자 함수

promise 객체의 state와 result는 내부 프로퍼티라고 앞서 설명했다. 그렇기 때문에 개발자가 직접 접근할 수 없다. 대신, .then, .catch, .finally와 같은 메서드를 사용해 사용할 수 있다. 이것은 위의 비유에서 consumer에 해당한다.

.then은 주로 promise가 resolve 됐을 때 실행되는 함수이다. 하지만, 만약 reject 됐을 때도 사용 가능하긴 한데,

promise.then(null, function(...))

처럼 첫 번째 인자에 null을 주면 된다. 즉, .then 메소드는 첫 번째 인자를 resolve로, 두 번째 인자를 reject로 실행해 value와 error를 모두 받을 수 있다.

하지만, reject 경우만 다룰 수 있는 .catch 메소드가 있다. 이 메소드는 위에 then에 첫 인자를 null로 설정한 것과 완전히 동일하다. 대신, 문법이 간결하다는 차이만 있다.

마지막으로 비교적 최근에 나온 .finally가 있다. 이는, 성공 실패와 상관없이 무조건 호출되며, 인자를 받지 않는다. 그렇기 때문에 성공, 실패 여부와 별개로 다음 메소드에 결과와 에러를 전달한다.

체이닝

이러한 방식을 chaining이라고 한다. promise 객체의 result가 사슬처럼 엮여 각각의 메소드를 통해 return 된다는 것이다.

이는 하나의 promise 함수에 소비자 함수 메소드를 여러 번 사용해 return 된 값에 또 다른 체인을 걸어 원하는 result를 도출할 수도 있고,

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

  alert(result); // 1
  return result * 2;

}).then(function(result) { // (***)

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});

하나의 promise 함수에 각각 메소드를 걸어 또 다른 원하는 결과를 도출해낼 수도 있다.

let promise = new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000);
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

promise.then(function(result) {
  alert(result); // 1
  return result * 2;
});

마지막으로, promise 메소드 안에서 또 다른 promise 함수를 생성 또는 반환할 수도 있다.

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000);

}).then(function(result) {

  alert(result); // 1

  return new Promise((resolve, reject) => { // (*)
    setTimeout(() => resolve(result * 2), 1000);
  });

}).then(function(result) { // (**)

  alert(result); // 2

  return new Promise((resolve, reject) => {
    setTimeout(() => resolve(result * 2), 1000);
  });

}).then(function(result) {

  alert(result); // 4

});

결과

추상적으로나마 promise 개념에 대해 정리했다. 앞으로 async, awat 등 콜백 함수의 대안격인 비동기 방식에 대해 정리하고 해당 개념을 적용하는 시간을 가져야한다!

참조

profile
개발이란?

0개의 댓글