[JS] Promise란? (then, catch, ...) - 프로미스 쉽게 이해하기


안녕하세요 파일입니다. 오늘은 JS에서 일종의 진입장벽이라고 할 수 있는 Promise에 대해 알아보겠습니다.

본 글은 우선 콜백 함수에 대해 어느정도 알고있다는 걸 전제로 합니다. 콜백 함수에 대해 모르시면 이 글을 참고해주세요.

 

동기 vs 비동기

우선 Promise에 대해 알아보기 위해 선행지식이 몇 가지 필요합니다. 그 중 하나가 바로 동기와 비동기 입니다.

동기와 비동기는 꼭 JS에서만 국한되는 개념은 아닙니다. 프로그래밍 언어를 좀 깊게 공부하시다가 보면 한번쯤은 만나게 되는 개념입니다.

 

쉽게 설명해서 동기는 순서대로 실행하는 것이고, 비동기는 순서대로 실행되지 않는 것 입니다.

JS의 경우에도 호이스팅 이후 순서대로 실행, 즉 동기적으로 작동합니다.

코드를 위에서 부터 아래대로 순차적으로 읽어내는 것이죠.

 

console.log(1);
console.log(2);
console.log(3);
console.log(4);

예를 들어서 이 코드를 JS에서 실행시키면 1, 2, 3, 4가 차례대로 동기적으로 실행될 것 입니다.

1을 출력 시키는 명령, 1이 출력될때까지 기다렸다가 다 출력되면 2를 출력, 다시 2가 다 출력되면 3을 출력.. 과 같은 형태로 진행합니다.

 

동기적인 코드는 동작 순서가 우리가 일반적으로 코드를 위에서 아래서 읽는 순서와 동일하기 때문에 동작을 파악하고 예측하기가 쉽다는 장점이 있습니다.

 

A();
B();
C();
D();

그러나 동기적으로 작동하는 코드엔 단점이 있습니다. 위 처럼 A, B, C, D 라는 작업을 함수로 만들어 놓고 순차적으로 실행한다고 생각해보겠습니다.

 

"동기적" 이라는 의미는 말 그대로 동시에 일어난다는 뜻으로, 요청하면 그 자리에서 결과가 바로 주어져야 합니다. 결과가 오지 않으면 결과가 올때까지 기다리고 결과가 다 와야 다음 코드를 실행하게 됩니다.

 

만약에 A라는 작업이 10분, B라는 작업이 20분, C라는 작업이 30분, D라는 작업이 40분 걸린다면

저 코드를 실행하면 A라는 작업이 10분 걸릴때까지 기다리고 결과가 오면, B라는 작업 20분, 다시 C라는 작업 30분, 마지막으로 D라는 작업 40분까지 결과가 끝날때까지 하염없이 기다려야 합니다

 

사용자 입장에선 저 코드를 실행하면 총 100분동안 프로그램이 결과를 기다리면서 결과가 돌아오기 전까지 아무것도 못하고 응답없음 창을 만나게 되겠죠. 100분 이후에나 창이 움직이면서 A, B, C, D의 결과가 일괄적으로 출력될 것 입니다.

 

이건 매우 나쁜 사용자 경험일 것입니다. 사용자 입장에선 그냥 A, B, C, D 동시에 실행해놓고 다른거 하고 있으면서 결과만 오는 대로 받고 싶으니깐요. 동기적으로 작동하는 코드들의 문제점을 해결하기 위해 프로그래밍 언어에서는 다양한 방법을 사용할 수 있습니다.

 

1. 프로그램 안에서 쓰레드(Thread) 란 것을 추가로 만들어서 그 쓰레드를 이용해서 처리하는 코드는 따로 돌려놓고 GUI 업데이트 등은 메인 쓰레드에서 처리하는 멀티 쓰레드 개념

 

2. 비싸지만 프로세스를 여러개 돌리는 멀티 프로세스 개념

 

3. 일단 다 실행해놓고 결과는 기다리지 않고 나중에 받는 비동기 개념

 

등등을 사용할 수 있습니다. (본 글은 Promise를 다루는 글이고 그렇게 중요한 내용은 아닙니다. 또한 본인도 저 위 3가지 개념을 전부 정확히 알고 있진 않습니다.)

 

JS 엔진은 기본적으로 싱글 쓰레드이기 때문에 2가지 일을 동시에 할 수 없고 1가지 일만 할 수 있습니다. JS에서는 위 3가지 중 하나인 비동기적인 처리를 해서 동기적인 동작의 문제점을 개선할 수 있고, 또 언어 자체가 비동기를 매우 쉽게 구현할 수 있게 설계되어 있습니다.

 

그러니 우선 오늘 글에서 멀티 쓰레드나 멀티 프로세스 같은건 잊어버리시고 비동기에 집중하시길 바랍니다.

 

console.log(1);
console.log(2);
setTimeout(() => console.log("3"), 1000)
console.log(4);

아까 코드에서 3번째 줄을 약간 변경하였습니다. setTimeout() 이라는 함수를 사용하고 있는데요.

setTimeout() 은 비동기적으로 작동하는 함수인데 첫 번째 인자값으로 콜백 함수를 주고, 두 번째 인자값으로 몇 초 있다가 이 콜백 함수를 실행할지 정하는 함수입니다.

 

1초있다가 () => console.log("3") 라는 함수가 호출될 것이고 (CallBack) console.log("3") 이 찍힐것입니다. 그런데 저 프로그램의 전체적인 실행 순서는 어떻게 될까요?

 

setTimeout() 함수는 시간 단위로 밀리세컨드를 사용하고 1000이 1초입니다.

실행 순서를 파악해보기 전에 비동기가 무엇인지 알아봅시다.

 

비동기 처리의 경우 동기와는 다르게 요청을 하면 결과가 동시에 일어나지 않을 것이라는 의미입니다. 

동기처리가 결과가 나올때까지 기다리고 결과가 나온 이후 실행했다면 비동기는 그냥 일단 다 실행해버리고 결과가 오는대로 받습니다.

 

이런식으로 처리하면 아까 A, B, C, D를 처리하는 문제를 해결 할 수 있습니다. A, B, C, D를 비동기적으로 처리한다면 한꺼번에 모든 함수가 실행될 것이고 결과만 나중에 받아내면 되니깐요. 이런것이 바로 비동기 작업의 장점이 되겠습니다.

 

다시 실행순서로 돌아와서.

 

JS는 코드를 위에서 아래로 실행하다가 비동기적인 setTimeout() 을 만나면 결과를 기다리지 않고 실행만 한 다음(예약 한 다음) 바로 다음라인으로 넘어가버립니다. (이때 setTimeout() 은 언젠가 실행이 되야 하므로 JS가 "이벤트 루프" 란 곳에 등록해서 모아두고 처리를 합니다.)

 

그래서 1, 2 가 출력되고 setTimeout() 을 만나서 바로 4를 출력하는 코드로 점프해서 실행. 이후 setTimeout() 에 설정된 대로 1초 있다가 콜백함수 () => console.log("3") 가 호출됩니다. 동기적으로 생각하면 1, 2, 1초 있다가 3, 그리고 4가 출력될 것이라고 생각할 수도 있지만 실제로는 비동기 작업에 의해 1, 2, 4, 3 이 출력되는 것이죠. 비동기 작업의 경우 이렇게 실행 순서를 보장하지 않기 때문에 코드를 제어하는것 (순서를 제어 하는 것)이 매우 힘들어집니다.

 

이것이 비동기 작업의 단점이 되겠습니다.

 

동기적 콜백과 비동기적 콜백

콜백은 항상 비동기에서만 사용될까요? 아닙니다.

콜백도 동기적 콜백 비동기적 콜백으로 나눌 수 있습니다.

 

function printImmediately(print){
	print();
}

printImmediately(()=> {console.log("나는 동기")})

동기적 콜백이란 위와 같은 코드입니다.

printImmediately 라는 함수는 print라는 함수를 인자값으로 받아서 나중에 함수를 호출하고 있습니다. (Called Back)

하지만 setTimeout() 처럼 뭔가 기다리거나 비동기적으로 작동하는게 아니라 동기적 함수이기 때문에 콜백 함수를 받아서 바로 호출해버리고 있죠. 

 

printImmediately(()=> {console.log("나는 동기")})

이런 코드를 작성하고 위 아래에 console.log 같은걸 찍어도 전부 동기적으로 작동하기 때문에 실행 순서가 보장될 것입니다. (우리가 코드를 읽는 순서대로 순서대로 동작할 것 입니다.)

 

setTimeout(() => console.log("3"), 1000)

비동기적 콜백은 아까전에도 봤던 비동기적 함수 setTimeout() 에 제공된 () => console.log("3") 와 같은 콜백 함수들을 의미합니다. setTimeout() 자체가 비동기적으로 작동하기 때문에 여기에 들어온 콜백 함수도 비동기적 콜백 함수라고 부르는 것이죠.

 

콜백은 자신이 원한다고 동기적이 되고 비동기적이 되는게 아닙니다. 어떠한 함수가 콜백을 인자로 사용하고 있냐에 따라 그 성질이 달라지는 것이죠. 콜백을 회사원으로 보자면 악덕 사장(고차 함수)이 바로 일하라고 시키면 동기적이 되는 것이고, 좀 기다렸다가 일하라고 하면 비동기적이 되는 것입니다.

 

콜백 함수 자체는 그냥 어떤 함수의 인자값으로 전달된 함수로써 어떤 함수(고차 함수)에 의해 나중에 호출될 뿐인 녀석입니다.

 

그래서 사실 비동기적 콜백이나 동기적 콜백으로 용어를 나눠서 설명할 필요가 있는가 싶네요. 

콜백 자체는 동기적, 비동기적 나눌 필요 없이 위에 밑줄 친대로 그냥 어떤 함수의 인자로 전달된 함수다. 이렇게만 이해해도 충분하다는게 제 생각입니다. 

 

콜백 지옥

setTimeout(callback, milliseconds)

아까전에 setTimeout() 에 콜백 함수와 시간(밀리세컨드) 을 등록해주면 비동기적으로 작동을 해서 제공된 시간이 지나면 그 안에 콜백 함수를 호출해서 실행을 해준다고 했죠.

 

그러면 만약에 1초 이후에 어떤 코드, 그 다음에 0.5초 이후에 어떤 코드 그 다음에 3초 있다가 어떤 코드를 실행하고 싶으면 어떻게 해야할까요?

 

setTimeout(() => {
  console.log(1);
  setTimeout(() => {
    console.log(2);
    setTimeout(() => {
      console.log(3);
    }, 3000);
  }, 500);
}, 1000);

비동기 적으로 작동하는 것들은 아까도 말했듯이 먼저 실행만 시키고 결과는 나중에 빨리 들어오는대로 받습니다. 우리가 프로그램을 작성하기 위해선 이런 비동기적인 처리의 장점을 이용하되, 비동기적인 처리들의 순서를 동기적으로 제어할 수 있어야 합니다. 

 

아까도 말했듯이 setTimeout() 은 지정된 시간 있다가 콜백 함수에 있는 내용을 실행합니다. 비동기적으로 작동해 코드 전체적으로는 순서를 보장하기 어려워도 적어도 콜백 함수가 지정된 시간 이후에 호출된다는 순서는 확실하게 보장이 되죠.

(비동기 함수에 콜백 함수를 넘기는 이유도 다음 작업을 지시하기 위해서니깐요.)

 

그래서 비동기적 처리들의 순서를 보장시키기 위해선 위 코드처럼 콜백 함수를 작성, 콜백 함수에 또 setTimeout을 넣고 거기에 또 콜백 함수를 제공해서 처리를 하는게 가장 간단한 방법일 것입니다.

 

우선 원하는 대로 작동하므로 동작에 있어선 큰 문제가 없습니다. 그러나 코드 가독성이 너무 구리다는 문제가 있습니다. 이렇게 콜백 안에 콜백이 들어가서 코드 가독성이 망가지는 것을 콜백 지옥이라고 합니다. 

 

이런 코드를 실무에서 그대로 사용할 순 없겠죠? 실무가 아니더라도 VSCODE에 Prettier 같은걸 깔아서 코드 가독성을 최대한 가져가려고 안달인데 저런 끔찍한 코드를 그대로 사용할 순 없습니다.

 

이미지 출처 : https://hanamon.kr/javascript-%EC%BD%9C%EB%B0%B1-%EC%A7%80%EC%98%A5-%ED%83%88%EC%B6%9C%ED%95%98%EA%B8%B0-%EB%B9%84%EB%8F%99%EA%B8%B0-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95/

항상 콜백 지옥 하면 나오는게 이런 장풍을 쏘는 사진입니다..

사진으로 보면 웃기지만 일종의 풍자(?) 죠

 

Promise

비동기 처리를 위해 사용되는 객체 "Promise"를 알아봅시다!

 

동기적인 처리였다면 그냥 코드 순서대로 실행이 될것이므로 걱정이 없지만, 만약에 비동기적으로 처리되는 것들의 경우 콜백 함수를 이용해서 순서를 제어해야 하는데 비동기적인 처리가 여러번 일어나는 경우 저렇게 콜백지옥에 빠지게 됩니다.

 

Promise를 이용하면 이런 연쇄적인 비동기 처리를 콜백 지옥 없이 깔끔하게 처리할 수 있습니다.

 

Promise의 경우 뜻 그대로 '약속' 이라는 의미입니다. 당장 데이터를 줄 수 없지만 제대로 처리가 되면 데이터를 넘겨주고 아니면 오류를 줄 것이라는 약속이요.

 

 

일반적으로 Promise가 들어간 코드를 "사용" 할 일이 Promise를 만들일보단 더 많을 것 입니다. 그러나 Promise를 만들줄도 쓸줄도 알아야 제대로 이해할 수 있고 Promise를 사용할 수 있다고도 할 수 있는것이겠죠.

 

일단 Promise를 만드는 입장에서 (Producer), Promise의 뜻에 대해선 나중에 생각하고 우선 코드로 Promise를 생성해봅시다.

 

const promise = new Promise(function (resolve, reject) {
  //안에서 비동기 작업 수행 (네트워킹, 파일 읽기 등 무거운 작업 수행)
});

일반적으로 Promise를 만드는 방법은 new Promise 키워드를 사용합니다. Promise는 객체이기 때문에 new로 생성을 해주고 콜백 함수 2개를 받는 함수를 제공해줍니다.

 

처음 볼땐 Promise 안에 (executor 라는 이름의) 콜백 함수를 넣고 또 그 인자값으로 받는 resolve, reject 모두 함수(콜백 함수) 라는 점이 처음 보실땐 상당히 특이할 것입니다.

 

저는 처음에 이런 구조들에 집착해서 머리가 상당히 아팠는데 Promise는 비동기 처리, 콜백 지옥을 벗어나기 위한 일종의 패턴이라 우선 패턴이라고 생각하고 암기해주세요.

 

저기 주석이 쳐진 부분에서 비동기 작업을 수행할 코드를 작성해주시면 됩니다. 비동기 작업이 제대로 끝났으면 콜백함수 resolve() 를 호출하고, 비동기 작업을 하다가 오류가 발생했으면 콜백함수 reject() 를 호출해주시면 됩니다.

 

사실 저 콜백 함수 resolve나 reject는 순서가 중요하지 이름은 딱히 중요하지 않습니다. 그러나 Promise를 사용할 때 관례적으로 사용하는 이름이므로 웬만하면 저대로 써주는게 좋습니다.

 

const promise = new Promise((resolve, reject) => {
  console.log("doing some things...");
  setTimeout(() => {
    resolve("Finished"); //일이 제대로 끝났으면 resolve 호출
  }, 2000);
});

 

ES6의 화살표 함수 까지 이용해서 저는 이렇게 코드를 추가해줬습니다. Promise는 비동기 처리에 이용하는 것이지만 문법적으로 비동기적인 코드가 안들어가면 실행이 안되거나 하는 것이기 아니기 때문에 동기적인 console.log() 가 들어가도 큰 상관은 없습니다.

 

저 코드를 실행해보면 바로 "doing some things..." 이 출력되는 걸 알 수 있습니다.

분명 Promise 객체만 만들고 promise라는 변수에 저장만 했는데 저렇게 코드가 바로 실행되는게 의아해 하실 수 있지만 저렇게 new로 Promise를 만드는 순간 안의 코드가 바로 실행되기 때문에 주의가 필요합니다.

 

사실 Promise를 이렇게 많이 쓰지 않고 쓸 필요도 없습니다. 저렇게 만들어지자마자 실행이 되버리는건 원치않으니깐요. 그래서 함수에서 Promise를 반환하도록 코드를 작성한다음에 함수를 호출해주면 return을 만났을 때 Promise를 생성함과 동시에 코드가 실행되게 될 것 입니다.

 

아마 글로 설명하면 이해가 잘 되지 않을 것인데 한번 코드를 보겠습니다.

 

function async_test() {
  return new Promise((resolve, reject) => {
    console.log("doing some things...");
    setTimeout(() => {
      resolve("Finished"); //일이 제대로 끝났으면 resolve 호출
    }, 2000);
  });
}

아까전의 코드를 함수로 감쌌습니다. 그리고 이 async_test() 라는 함수는 Promise를 반환(return)하기 때문에 다음과 같이 읽어낼 수 있습니다.

 

안녕 난 async_test() 라는 함수야~ 나를 호출하면 당장은 데이터를 줄 수 없지만 제대로 처리가 되면 데이터를 넘겨주고 아니면 오류를 줄 것이라는 '약속' 을 하나 할게! (Promise를 반환할게)

=> 데이터는 resolve() 함수를 호출해서 넘겨줄거고 오류가 발생하면 reject() 함수를 호출해서 알려줄거야!

 

Promise 안에서 비동기 작업을 수행한 후 일이 제대로 끝났으면 resolve() 함수를 호출하고, 일이 처리하다가 오류가 생겼으면 reject() 함수를 호출합니다. 이 2개의 함수를 이용해서 데이터를 전달할 수 있습니다.

여기까지가 Promise를 만드는 쪽에서의 입장과 구조입니다.

 

그러면 저 async_test() 라는 함수를 쓰는쪽에선 어떻게 할까요?

분명 async_test()는 자신을 호출하면 당장은 데이터를 줄 순 없지만 언젠가 처리가 되면 데이터를 넘겨주고, 아니면 오류를 줄 것이라고 했습니다. 이 언젠가 들어올 데이터나 오류를 저 함수를 쓰는 입장에선 어떻게 받아내야 할 지 알아봅시다.

 

const async_promise = async_test();
async_promise.then((data) => console.log(data));

사실 사용하는쪽은 만드는 것보다 훨씬 간단합니다.

아까 일이 제대로 끝났으면 Promise 안에서 resolve() 함수를 통해 데이터를 전달한다고 했는데 이 resolve() 함수로 들어오는 데이터는 Promise의 then() 함수(메소드) 와 , 그 안에 들어가는 콜백 함수로 데이터를 받아낼 수 있습니다.

한마디로 resolve()는 then() 과 그 안의 콜백 함수에 대응됩니다.

 

저기 data에 "doing some things..." 가 들어오는 것이죠.

 

//Producer
function async_test() {
  return new Promise((resolve, reject) => {
    console.log("doing some things...");
    setTimeout(() => {
      resolve("Finished"); //일이 제대로 끝났으면 resolve 호출
    }, 2000);
  });
}

//Consumer
const async_promise = async_test();
async_promise.then((data) => console.log(data));

이 코드를 실행해보시면 아까 전에 함수 없이 그냥 실행했을때와는 다르게 2초 후에 "doing some things..." 이라는 데이터가 잘 들어오는걸 볼 수 있습니다.

사실 async_test() 라는 함수의 return 값은 Promise 그 자체이기 때문에 저렇게 따로 변수를 저장해서 저장하고 호출해줄 필요 없이

 

//Producer
function async_test() {
  return new Promise((resolve, reject) => {
    console.log("doing some things...");
    setTimeout(() => {
      resolve("Finished"); //일이 제대로 끝났으면 resolve 호출
    }, 2000);
  });
}

//Consumer
async_test().then((data) => console.log(data));

이렇게 쓰는게 일반적입니다.

 

일이 제대로 끝나서 Promise 안에서 resolve() 함수를 호출하면 그 콜백함수는 then 함수 안의 콜백 함수와 대응 되어 데이터를 받아낼 수 있는건 알겠습니다.

 

그러면 오류가 나면 Promise 내부에서 reject() 를 호출하라고 했는데 이럴땐 오류 메세지를 어떻게 받아내야 할까요?

resolve() 에 대응되는게 then() 과 그 안의 콜백 함수였다면

reject() 에 대응되는건 catch() 와 그 안의 콜백 함수입니다.

 

예외 처리를 할 때 try catch 문의 그 catch를 생각해주시면 됩니다.

 

//Producer
function async_test() {
  return new Promise((resolve, reject) => {
    console.log("doing some things...");
    setTimeout(() => {
      reject(new Error("Error Message")); //일이 제대로 끝났으면 resolve 호출
    }, 2000);
  });
}

//Consumer
async_test() //
	.then((data) => console.log(data));

setTimeout() 이후 모종의 오류가 일어나서 reject() 함수를 호출했다고 가정해봅시다. reject() 함수엔 Error 라는 오류 객체를 만들어서 그 안에 메세지를 담아 던지고 있습니다.

 

저 코드를 실행하면 Uncaught (in promise) Error: Error Message 라는 에러가 잡히지 않았다는 오류 메세지가 뜹니다.

프로그램 실행하다가 예외(Exception)이 발생하면 try catch 문을 통해서 그 예외를 잘 처리해주지 못하면 프로그램이 튕기잖아요? Promise의 경우도 마찬가지입니다.

Catch를 통해서 오류 처리(Error Handling) 을 해주셔야 합니다.

 

//Producer
function async_test() {
  return new Promise((resolve, reject) => {
    console.log("doing some things...");
    setTimeout(() => {
      reject(new Error("Error Message")); //일이 제대로 끝났으면 resolve 호출
    }, 2000);
  });
}

//Consumer
async_test()//
    .then((data) => console.log(data))
    .catch((error) => console.log(error));

이렇게.. 제대로 실행됬으면 then로 데이터를 받고, 만약에 아니면 catch 에 잡혀서 오류를 받습니다.

 

 

 

Promise Chanining

setTimeout(() => {
  console.log(1);
  setTimeout(() => {
    console.log(2);
    setTimeout(() => {
      console.log(3);
    }, 3000);
  }, 500);
}, 1000);

아까 전에 콜백 함수로 난잡했던 이 일련의 비동기 처리들을 아까 배웠던 Promise 패턴을 이용해서 바꿔봅시다.

 

function PromiseTimeOut(msg, time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(msg);
    }, time);
  });
}

PromiseTimeOut("1", 1000) // <-- 여기에 주석을 달아주면 VSCODE Prettier이 예쁘게 프로미스 함수들을 엔터들로 정리해준다
  .then((data) => {
    console.log(data);
    PromiseTimeOut("2", 500).then((data) => {
      console.log(data);
      PromiseTimeOut("3", 3000).then((data) => console.log(data));
    });
  });

어.. 분명 작동도 되고 Promise를 쓰긴 했는데 뭘까요? 아까 콜백 함수 때문에 난잡했던 코드와 별다를바가 없어보이네요.

보시면 프로미스의 then 안에서 또 Promise의 then 이 나와서 처리가 이루어지고 있습니다.

이렇게 Promise.then 내부에서 또 then 이 나오는 패턴을 nested Promise(중첩된 Promise) 라고 부릅니다.

 

이러한 패턴이 나오면 Promise를 제대로 이해하고 있지 못하거나 잘 사용하지 못하고 있는 것 입니다. Promise chain을 이용해서 이 패턴을 해결해봅시다.

 

function PromiseTimeOut(msg, time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(msg);
    }, time);
  });
}

PromiseTimeOut("1", 1000) // <-- 여기에 주석을 달아주면 VSCODE Prettier이 예쁘게 프로미스 함수들을 엔터들로 정리해준다
  .then((data) => {
    console.log(data);
    return PromiseTimeOut("2", 500);
  })
  .then((data) => {
    console.log(data);
    return PromiseTimeOut("3", 3000);
  })
  .then((data) => console.log(data));

아까와 다르게 코드가 매우 깔끔해졌습니다.

Promise Chain 을 위해선 이렇게 then 안에서 return을 통해서 다음 then에 값을 전달할 수 있습니다.

 

코드의 순서를 파악해보면

PromiseTimeOut("1", 1000) // <-- 여기에 주석을 달아주면 VSCODE Prettier이 예쁘게 프로미스 함수들을 엔터들로 정리해준다
  .then((data) => {
    console.log(data);
    return PromiseTimeOut("2", 500);
  })

처음에 PromiseTimeOut("1", 1000) 부분을 호출하고 PromiseTimeOut() 은 Promise를 반환합니다. 여기서 나올 값을 then 으로 잡고 값을 콜백 함수를 이용해 출력한 뒤에 다시 PromiseTimeOut("2", 500); 을 반환합니다. 이 함수값 역시 Promise 이기 때문에 다음 .then 에서 잡을 수 있습니다.

 

  .then((data) => {
    console.log(data);
    return PromiseTimeOut("3", 3000);
  })
  .then((data) => console.log(data));

그래서 다음 then에서 값을 잡고 출력, 다시 PromiseTimeOut() 호출 값을 반환해서 마지막 then 에서 3이라는 값을 잡고 출력하게 되어 코드 실행은 끝나게 됩니다.

 

저렇게 .then() 을 계속해서 쓸 수 있는 이유가 궁금하실 수 있는데 then() 함수를 호출하면 반환값 자체가 Promise이기 때문에 저렇게 .then() 을 연속적으로 호출해서 사용할 수 있다 라고 생각하시면 됩니다.

 

 

 

Promise의 상태

출처 :&nbsp;https://velog.io/@limes/Javascript-Promise

  • 대기(pending) : 비동기 처리의 결과를 기다리는 중 (일을 처리중)
  • 이행(fulfilled) : 비동기 처리가 정상적으로 끝났고 결과를 가지고 있음 (약속이 제대로 지켜짐)
  • 거부(rejected) : 비동기 처리가 비정상적으로 끝났음 (약속이 모종의 이유로 제대로 지켜지지 않음)
  • 처리(settled) : 약속이 지켜졌던 안 지켜졌던 간에 결론이 난 상태

 

원래 이런건 글 초장에서 설명드려야 하지만 Promise는 사실 위처럼 상태를 가집니다.

new Promise() 로 Promise 가 생성되는 직후부터 Promise는 대기(pending) 상태가 되어서 일을 처리하다가, resolve() 함수가 호출됐다면 이행(fulfilled) 상태가 되고 reject() 함수가 호출됬다면 거부(rejected) 상태가 됩니다.

 

 

여기까지 왔는데 Promise가 이해가 안돼요

사실 저는 Promise에 대해 이해하려고 블로그 글이나 관련 강의 영상을 수십개도 넘게 본 거 같습니다. 프로그래밍 공부야 밥먹듯이 하는데 제대로 이해가 안간건 이번이 처음이였거든요..  (사실 C#의 Interface나 IEnumerable 같은것도 잘 모릅니다. 이 업계는 알게 산넘어 산입니다 ㅎㅎㅎ...)

 

저는 어렵게만 쓴 블로그 글이나 자기만 보려고 메모해둔 식의 글을 정말 싫어합니다.

그래서 최대한 Promise에 대해 쉽게 이해할 수 있도록 복잡한 내용은 줄이고 좀 허접하게 스토리 텔링(?) 형식으로 글을 작성했습니다.

 

아마 초심자 분들이 보기엔 어렵게 써논건 뭐 거기서 거길수도 있겠습니다...

 

여기까지 읽어보고 별짓거리를 다해봤는데  Promise가 제대로 이해 안되시면

 

아래 유투브 강의 3편을 우선 보시고

https://www.youtube.com/watch?v=s1vpVCrT8f4
https://www.youtube.com/watch?v=JB_yU6Oe2eE
https://www.youtube.com/watch?v=aoQSOZfz3vQ

 

그래도 이해가 안간다? 하면 일단 그냥 외우세요. Promise는 비동기 처리의 일종의 패턴이기 때문에.. 이번에 공부를 하면서 느낀건데 그냥 외우는것도 나쁘지 않다고 봅니다. 외워서 쓰다보면 어느 순간 깨달음이 옵니다. 그래도 찜찜한 마음이 들거나 이해하고 싶은 오기가 생기면 그때 다시 이 글이랑 유투브 강의 복습해보세요. (마음의 여유가 생겼을 때 이야기 입니다.)

 

그럼 이해가 될겁니다. 화이팅!

 

 

<참고>

https://dodo-itstory.tistory.com/2

https://www.youtube.com/watch?v=s1vpVCrT8f4&t=287s 

https://elvanov.com/2597

https://joshua1988.github.io/web-development/javascript/promise-for-beginners/

https://medium.com/sjk5766/promise-hell%EA%B3%BC-promise-chain-73a3349d7f01

COMMENT WRITE