16 Promise

프로그램에서 간단한 코드가 한줄한줄 순차적으로 실행된다면 크게 어렵지 않겠지만, 하지만 자바스크립트에서 함수를 호출했는데 그 함수가 시작되고 끝나는 동안에도 프로그램을 계속 진행될 필요가 많이 있다.

이럴때 Promise를 이용해서 비동기적 상황에서 코드를 좀 더 명확하게 파악하고 실행하도록 만들 수 있다.

mdn - Promise
문서에 따르면, Promise개체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타낸다.

/**
 * 1.
 * ES6 부터 JavaScript의 표준 내장 객체로 추가되었다.
 * ES6 를 지원하는 브라우저나 Node.js에서 전역에 있는 Promise를 확인할 수 있다.
 *
 * Promise개체 혹은 Promiise객체라고 불린다.
 */

console.log(Promise);

/**
 * 2.
 * 생성자를 통해서 (나만의)프로미스 객체를 만들 수 있다.
 * 생성자의 인자로 executor라는 함수를 이용한다.
 */

new Promise(/* executor */);

/**
 * 3.
 * executor함수는 resolve와 reject를 인자로 가집니다.
 * (resolve, reject) => {...}
 * resolve와 reject는 함수이다.
 * resolve (), reject()
 */

new Promise(/* executor */ (resolve, reject) => {});

/**
 * 4.
 * 생성자를 통해서 프로미스 객체를 만드는 순간 pending(대기) 상태라고 한다.
 */

new Promise((resolve, reject) => {}); //pending

/**
 * 5-1
 * 이제 pending상태로 돌입한 후,
 * executor함수 인자 중 하나인 resolve 함수를 실행하면, fulfilled(이행) 상태가 된다.
 */

new Promise((resolve, reject) => {
    //이 객체가 생성되면 무조건 pending상태로 돌입하고
    //...(비동기적 처리가 되는 상황벌어짐)
    //그 이후 처리가 정상적으로 끝났을때
    resolve(); //resolve라는 함수를 호출해주면 이 상태가 바로 fulfilled(이행된) 상태로 돌입된다.
});

/**
 * 5-2
 * executor함수 인자 중 하나인 reject함수를 실행하면, rejected(거부) 상태가 된다.
 */

new Promise((resolve, reject) => {
    reject(); // rejected
});

 

위는 코드로 설명해보았지만, 아래 다이어그램으로 흐름을 한 번 확인해보자 

new Promise로 executor함수를 인자로 받아서 만들었을때 pending상태로 바로 돌입을 하게되고, 빨간색 사이에서 비동기적 상황이 일어날 것이다. 그렇게 일어나는 동안에는 계속 pending을 유지하게된다. 
이 pending상태에서 비동기적 상황이 끝났을때, 성공적으로 임무 완료시  fulfilled(이행)라고하는 상태의 resolve함수를 호출하고, 만약 상황이 제대로 이행되지않고, 문제가 생겨서 에러상황이 발생했다라고하면 reject라고하는 함수를 호출해서  reject상태로 만들어 줄 수 있다.

 

그럼 이제 프로미스 객체를 한 번 만들어보자!

/**
 * (p 라는 프로미스 객체) 프로미스 객체를 만들고, 펜딩된 상태가 된 후에
 * 1000ms 후에 정상적으로 fulfilled 상태로 넘어가는 코드 작성
 */

new Promise((resolve, reject) => {
    /*pending */
    setTimeout(() => {
        resolve(); /*fulfilled*/
    }, 1000);
});

 

 이렇게 프로미스 객체를 만든 후, 프로미스 객체를 사용하는 방법에 대해서 알아보자!

/**
 * p 라는 프로미스 객체가 fulfilled 되는 시점에 p.then안에 설정한 callback함수가 실행된다.
 *
 */
const p = new Promise((resolve, reject) => {
    /*pending */
    setTimeout(() => {
        resolve(); /*fulfilled*/
    }, 1000);
});

//then이라는것을 언제 받게되냐?
//resolve()가 호출된 fulfilled상태로 넘어가면 그때 then으로 넘어와서 then(...여기 함수) 가 실행된다.

p.then(/*callback */);

//위의  then을 풀어보자면 아래와 같다.
p.then(() => {
    //이 부분은 위의
    //   new Promise((resolve, reject) => {
    //     /*pending */
    //     setTimeout(() => {
    //         resolve(); /*fulfilled*/
    //     }, 1000);
    // });
    // 이 객체가 resolve()된 후에 실행되므로 1초 후에 실행된다.
    //따라서 이 구간은
    //일종의 여기는 /*callback*/ 을 작성하는 구간이다.
});

 

 

이제 실제로 코드가 어떻게 작성되는지 알아보자 

시작한지 1초후에  fulfilled상태로 되면서, then안에있던 callback함수가 불리게 된다.

 

그런데 이제 Promise객체가 만들어지는 순간

 new Promise((resolve, reject) => {
    /*pending */
    setTimeout(() => {
        resolve(); /*fulfilled*/
    }, 1000);
});

은 사용하는곳이 바로 뒤에

p.then(() => {
    console.log("1000ms 후에 fulfilled된다.");
});

나오기 때문에 큰 문제는 없지만,

실제로 실무에서 사용할때는 객체를 바로 만들어서 사용하기 보다는 실제로 사용하는 곳에서 객체를 생성해서 then과 엮어주는 방식으로 작업을 하게된다.

따라서 아래와 같이 작성한다.

그리고 rejected되는 시점 또한 작성해보자.

이러한 비동기 함수가 성공적으로 임무를 완수했을때와 잘못돼서 잘못됐다고 알려줄때는 상태의 차이가 있기 때문에 그거에 맞게 코드를 진행해주어야한다.

 


이제, executor의 resolve함수를 실행해서 fulfilled상태로 바꿀때 then뒤에 있는 callback에 메시지를 전달할 수 있는데, 이렇게 전달하는게 메시지 뿐만아니라 다음동작을 이어가는데 필요한 결과물을 전달하는 용도로 많이 쓰인다.

 

보통 우리가 비동기작업을 한다하면, 원격에 있는 데이터를 가지고올때 많이 사용하는데, 
원격으로 요청하고 정상적으로 받았을때 받아온 데이터를 then으로 데이터로 넘겨서 주고, 그것을 활용해서 어떤 UI를 만든다던지 할때 많이 사용한다.

따라서 데이터 넘기는 방법은 자주쓰이므로,꼭 알아둬야한다! 


아래는 reject함수를 실행할때 인자를 넣어 실행하는 것을 알아보도록하자.

 

하지만 보통 JavaScript에서 사용하는 error객체가 있는데 그 error객체를 만들어서 넘기는것이 일반적이다.

아래는 그 error를 바꾼 방법이다.

Error객체가 log로 찍힐때 나오는 모습은 아래와 같으며,

Error: bad
    at Timeout._onTimeout (C:\Users\oracle\Documents\_TIL\TIL_\basic-js\conditional-statements\promiss.js:9:20)
    at listOnTimeout (node:internal/timers:557:17)
    at processTimers (node:internal/timers:500:7)

Error객체에 여러가지 상태를 담거나 코드를 적어서 넘겨서 처리하는것은 앞으로 코딩을 하며 일반적인 코딩방식이 될 것이다.


지금까지 p라는 함수를 실행하면서 then과 catch로 이어가는 모습을 보았다.

then이나 catch를 실행한 후, 뭔가 최종적으로 해줄 일이 더 있다면 finally()를 이용해서 최종적으로 할 일을 설정해줄 수 있다.

rejected된 error객체가 log로 찍힌 후, 뒤에 finally end가 찍힌거를 확인할 수 있다.

 

Promise가 없을때 어떤식으로 작업했는지 생각해본다면  callback의 연속 , callback헬......이라고도 불리는데 

어떤상황인지 한 번 작성해보자!

위의 callback을 활용한 비동기 작업을 promise활용한 비동기 작업으로 만들어보자.

즉, 확대해서 보자면

이러한 형식인데,

즉, then함수에 다시 프로미스 객체를 리턴하는 방식을 사용하면 이전의 콜백헬처럼 계속 안으로 들여쓰기가 되기보다는 계속 순차적으로 체이닝해서 처리가 가능하게된다.

이게 좀 더 보기 편하다! 이런식인 것이다.

 

 


 

 

지금까지 Promise객체를 만들때는 new Promise를 이용해서 executor를 넣어줘서 만드는 방법으로 했었는데, 이것 말고도 몇가지 방법이 또 있어서 알아보도록하자.

 

⭐⭐⭐Promise.resolve( /* value */);
>앞에 new는 없고, Promise라는 전역 객체의 안에있는 resolve라고하는 함수를 실행하면서
Promise를 만들어 내는것이다.

 

 

결과를 보면, then 메서드가 없는경우...  <- 가 먼저 튀어나온다. 왜냐하면, 얘는 1초뒤에 실행된다! 이런게 없기 때문에  바로 "bar"라고하는 문자열을 담아서 마지막 then쪽으로 넘긴것이다.

 

마지막으로 promise객체인 경우에는 그 promise객체가 resolve된 후에 결과를 then으로 넘겨서 처리하게된다.

 

⭐❗이렇게 된다는 것은 만약 어떤 객체가 Promise객체인지 그냥 data 객체인지 모를때 즉, 확실하지 않을때는 이렇게 Promise.resolve()로 한 번 실행해서 넘기면 resolve가 되게하거나 그냥 값이 바로 then으로 넘어가거나 이렇게 되기 때문에 이럴때 유용하게 사용할 수 있다.


⭐⭐⭐Promise.reject( /* value */);

Promise.reject를 사용하면, catch로 연결된 rejected상태로 변경된다.

이렇게 error가 뜨게되는데, 보통 바로 promiss로 reject를 해서 error를 날리는 case는 많지 않으므로 이런문법이 가능했다! 는 사실을 알아두기만 하면 좋을 것 같다.


Promise.resolve 와 Promise.reject말고, Promise.all이라고하는 함수를 통해서 Promise를 다뤄보자! 

>프로미스 객체가 하나가 아니고, 여러개를 생성하고, 그 프로미스 객체를 배열로 만들고 Promiss.all에 인자로 넣어서 실행을 하면 이 안에있던 Promise객체 하나하나가 모두 fulfilled상태가 되었을때  Promiss.all에 연결된 then이 그 안에있는 callback을 부르게된다. 그리고 then에 callback에 인자로 Promise객체의 여러개 resolve된 인자값을 하나하나 다 모아서 (순서대로)배열로 만든후 인자값으로 넣어서 Promiss.all에 then에 들어있는 인자 값으로 돌려주게된다.

 

결과를 보게되면 1초 이후에 바로 되는것이 아닌, 1초 2초 3초 뒤에 즉 각각 모두 fulfilled된 상태 이후에 한번에 then이 불리게된다.( 1초먼저 fulfilled됐을꺼고,  2초먼저 fulfilled됐을꺼고,  3초먼저 fulfilled됐으니까 이후에 then이 되는것!)

 

이제, resolve에다가 인자를 넣어서 보내보겠다.

 

결과를 보게되면 3초 후에 Promise.all에 연결되어있는 then안에있는 callback함수가 실행될것이고, 인자로 각각의 millisecond가 배열로 만들어져서 넘어오게된다.

 

따라서 , 뭔가 비동기작업 여러개를 동시에 시작해서 모든 것이 다 끝나면 처리해줄것이 있다면 이런식으로 처리하면된다.

 


Promise.all는 아니지만 비슷한 Promise.race를 다뤄보자!

 

 

Promiss.all은 프로미스 객체가 모두 fulfilled 될 경우 진행이 됐었는데, Promise.race는 그중 가장 빠른거 먼저 fulfilled되면 그때 바로 then함수가 실행된다.

 

이렇게 Promise객체에 대해서 알아봤다. 

Promise는 자바스크립트에서 너무 중요하며, 비동기를 처리하기위해서 가장 기본이 되는 개념이 Promise이다.

이 다음에 async await를 배우는데, Promise가 기본이 된후 알아야되는 개념이다.

 

 


참고자료 : 

패스트캠퍼스

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기