본문 바로가기
Javascript/Javascript

[ES6+] Promise와 then, catch, finally, all, race

by img 2021. 10. 23.
const obj = new Promise((resolve, reject)=>{	//obj라는 변수에 Promise라는 인스턴스를 생성하여 할당
// resolve와 reject 라는 이름의 함수를 만들어서 해당 인스턴스에 설정
// 처리가 성공하면 resolve가, 실패하면 reject가 호출됨
  console.log("Promise 읽는 중")
  resolve("성공");	
  reject("실패");		
})


obj.then(
  (value)=>{ console.log("결과는", value) },
  (reason)=>{ console.log("결과는", reason)}
);

console.log("끝까지 읽음")

// 끝까지 읽음
// Promise 읽는 중
// 결과는 성공

해당 코드를 위에서부터 하나하나 보면,  우선 obj라는 변수에 Promise라는 인스턴스를 생성하여 할당한다. 그리고 만들어진 Promise안에 resolve와 reject 라는 이름의 Promise의 콜백함수를 받아와서 해당 인스턴스에 설정한다. 처리가 성공하면 resolve가, 실패하면 reject가 호출된다. 

이 모든 것은 console.log("끝까지 읽음")이라는 것이 찍힌 후에 실행이 된다. Promise라는 것은 지금 실행하지 않고 말그대로 코드를 끝까지 실행한 후 여기로 돌아와 실행하겠다는 "약속"이다. Promise로 인해 비동기 처리가 되는 것이다. 

obj.then은 obj라는 Promise가 실행되고 나서 실행되는 Promise의 메소드이기 때문에, console.log("Promise 읽는중") 이라는 콘솔이 찍히고 나서 실행된다. then에서는 두개의 함수 파라미터를 받을 수 있는데,  Promise에서 resolve()를 호출하면 then()의 첫번째 파라미터 함수가 실행되고, reject를 호출하면 then의 두번째 파라미터함수가 실행된다.

만약 resolve나 reject에 파라미터를 여러개 작성한다고 해도 파라미터는 하나만 사용한다.

const obj = new Promise((resolve, reject)=>{
  resolve("하나", "둘", "셋");	// 파라미터 세개 
})
obj.then(
  (value)=>{ console.log(value) },
  (reason)=>{ console.log(reason }
)

// 하나 
// 파라미터를 세개를 보내더라도 처음 하나만 사용한다.

그래서 다수의 파라미터 값을 넘겨주려면 참조하고 있는 "하나"의 주소값을 가지고 있는 배열이나 Object 등을 사용해야 한다. 


그리고 reject될 경우 .then()의 두번째 파라미터로 받는 방법 말고, .catch()로 받는 방법도 있다.

const check = false;
const obj = new Promise((resolve, reject)=>{
  check ? resolve(check) : reject("하나","둘","셋");
})
obj
  .then((value)=>{ console.log(value) })
  .catch((value)=>{ console.log(value });
  
// 하나

reject가 호출 되면 catch()가 실행되며, 마찬가지로 파라미터를 여러개 보내도 처음 하나만 받는다. 


.fianlly()는 .then과 .catch 이후에 성공 실패에 상관 없이 무조건 실행되는 함수이다. then과 catch 이후에 실행해야 하는 코드가 동일하다면 finally를 사용해서 코드의 중복을 막을 수 있다. 

const obj = new Promise((resolve, reject)=>{
  resolve(100);
})
obj
  .then((value)=>{ 
    console.log(value);
    return 200;
  })
  .catch((reason)=>{
    console.log(reason);
  })
  .finally((param)=>{
    console.log("finally param은?", param)
  })
  
  // 100
  // finally param은 undefined

resolve()로 인해 then()의 핸들러 함수가 실행되고, return 200으로 인해 [[PromiseValue]]에 200이 설정"하려고했다". 그리고 resolve이기 때문에 catch()는 건너 뛰고 바로 .then이후에 바로 .finally()로 간다. param을 받아와서 찍어봤지만 return했던 200 대신 undefined가 찍힌다. finally는 문법적으로 파라미터를 사용하지 않기 때문에 [[PromiseValue]]에 return한 200이 설정되지 않았고, 파라미터를 작성하더라도 에러는 나지 않지만 undefined가 설정되므로 의미가 없다. 


파라미터의 "모든" Promise의 처리를 완료했을 때 then의 핸들러 함수를 실행할 수 있는 .all() 메소드도 있다. Promise.all()의 형태로 작성하고 Iterable 파라미터만 가져갈 수 있고 Promise 인스턴스를 반환한다. 

function order(delay){
  return new Promise((resolve)=>{
    setTimeout(()=>{
      console.log("실행자 : ", delay);
      resolve(delay);
    }, delay);
  });
};

Promise
  .all([order(500), order(300), order(100)])
  .then((param)=>{ console.log("then : ", param })
  
// 실행자 : 100
// 실행자 : 300
// 실행자 : 500
// then : 500,300,100

0.5초 뒤 Promise 인스턴스를 반환, 0.3초 뒤 Promise 인스턴스 반환, 0.1초 뒤 Promise인스턴스 반환하는 함수를 한꺼번에 Promise.all()을 통해서 탔다. all()에 들어있는 Iterable 파라미터가 모두 다 실행한 후 .then()은 한번만 타는 것을 확인 할 수 있다. 

다만 reject()일 경우는 약간 다른데,

function order(delay){
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      console.log("실행자 : ",delay);
      delay === 300 ? reject(delay) : resolve(delay);
    }, delay);
  });
};

Promise
  .all([order(500), order(100), order(300)])
  .then(
    ( param )=>{ console.log("성공", param },
    ( param )=>{ console.log("실패", param }
  )
  
  // 실행자 : 100
  // 실행자 : 300
  // 실패 : 300
  // 실행자 : 500

reject()가 하나라도 발생하면 전체가 끝나지 않아도 .then()의 두번째 파라미터를 먼저 탔다가, 다시 돌아와서 남은 나머지를 실행시키고, 나머지가 resolve()더라도 첫번째 파라미터 함수는 타지 않는다.


Promise.race()는 .all()과 달리 resolve, reject와 관계 없이 처음 한번만 then()을 실행하고, 그 이후엔 더 이상 실행하지 않는다.

function order(delay){
  return new Promise((resolve)=>{
    setTimeout(()=>{
      console.log("실행자 : ", delay);
      resolve(delay);
    }, delay);
  });
};

Promise
  .race([order(500), order(100), order(300)])
  .then(( param )=>{ console.log("then : ", param) })
  
// 실행자 : 100
// then : 100
// 실행자 : 300
// 실행자 : 500

obj (Promise오브젝트)형태를 살펴보면,

obj.__proto__에 Promise.prototype에 연결된 메소드가 표시된다. 그 중 [[PromiseStatus]] : "resolved" 라는 프로퍼티가 있다. Promise의 상태가 fulfilled 되었다는 뜻이다.

이외에 Promise.prototype에 [[PromiseValue]]라는 프로퍼티가 있는데, resolve(value)의 값을 then에서 다시 사용할 수 있도록 PromiseValue에 저장을 해놓는 것이다. Promise인스턴스를 먼저 만들어 놓고 코드를 끝까지 읽은 후에 다시 돌아와서 resolve나 reject를 실행하기 때문에 다시 돌아와서 resolve나 reject가 호출되었을 때 파라미터 값으로 사용하려고 저장해놓는 용도이다.


 

댓글