Promise

Chanhee Jang·2023년 4월 5일
0

JS

목록 보기
7/9


콜백함수로 프로그램의 비동기 코드를 작성하면 순차성과 믿음성(언제 내 코드가 실행될건지)이 결여되는 중요한 결함이 있다.

콜백의 문제는 제어의 역전이다. 내 콜백 함수를 다른 곳에 전달하고 정상적으로 콜백이 실행되길 기도할 수 밖에 없다.

하지만 제어의 역전을 되역전시킬수 있다면? 다른 파트에 프로그램 진행을 넘겨주지 않고, 개발자가 언제 작업이 끝날지 알 수 있고, 무슨 일을 해야할 지 스스로 결정할 수 있다면?

그게 바로 프로미스다.


Promise - 비동기적으로 미래에 내가 원하는 값을 받거나 내가 원하는 에러 처리를 하는 방법

기존의 콜백만을 사용하여 비동기 프로그래밍을 하면 다음과 같은 문제가 있다.

  • 너무 일찍 콜백을 호출
  • 너무 늦게 콜백을 호출
  • 너무 적게, 많이 콜백을 호출
  • 필요한 환경/인자를 정상적으로 콜백에 전달 못함
  • 발생 가능한 에러/예외 무시

ES6에 나온 이쁜이 프로미스는 이것들에 대한 해결책을 제시하고 있다.


생김새

let p = new Promise(function (resolve, reject) {
	// 무언가 처리중...
	...
	resolve(value); // resolve() 함수를 통한 값 전달

	if(!value) reject(new Error("큰일이 난 모양입니다."));
});

Promise 생성자는 항상 new 키워드와 함께 쓰며 콜백 함수를 인자로 받는다.

Promise 생성자의 인자로 받는 콜백 함수는 두 개의 인자를 가져야 한다.

resolvereject는 js쪽에서 제공하는 콜백 함수다.

resolve - then() 핸들러로 값을 전달해 줄 수 있는 함수이다. Promise 안에서 resolve 호출을 통해 약속을 이룰 수 있다.

reject - catch() 핸들러로 값을 전달해 줄 수 있는 함수이다. Promise 안에서 reject 호출을 통해 약속을 버릴 수 있다.

(then()catch()는 밑에 작성되어 있다.)


상태

Promise는 상태를 가지고 있다. Promise의 상태를 통해 개발자는 해당 promise의 작업 현황을 알 수 있다.

Promise의 상태는 세 가지다. pending (미결) , fulfilled (이행됨) , rejected (거부됨)

생성된 Promise는 기본적으로 pending 상태다.

Promise의 내부 로직에 의해 resolve 함수가 실행되면 fulfilled,

그러지 못하고 reject 함수가 실행되면 rejected 상태가 된다.


만들어진 Promise는 어떻게 사용하는가?

만들어진 프로미스에 .then() , .catch() , finally() 핸들러를 붙이면 된다.

let p = new Promise(function (resolve, reject) { ... });
// (1)-1
p
 .then(v => {
	console.log(v);
})
 .catch(err => {
	console.log(err);	
});

(1)-1: 프로미스가 정상적으로 fulfilled 되었다면 then 핸들러로 넘어갈 것이다. then 핸들러는 2개의 콜백 함수 인자를 가질 수 있다.

(1)-1의 경우 1개의 콜백함수만 들어갔다. 에디터의 자동완성에 보면 onFulfilled 라고 적혀있는 걸 볼 수 있다.

onFulfilled 친구는 resolve되었다면 받아볼 수 있는 함수다.

(v) ⇒ {} 의 형태로 작성하면 된다.


// (1)-2
p
 .then(v => {
	return v + 1;
})
 .then(v => {
	console.log(v);
})
 .catch(err => {
	console.log(err);	
});

then 핸들러에서 리턴한다면 이 또한, then 핸들러를 추가로 이어줄 수 있다.

이런 형태로 then과 catch를 덕지덕지 이어놓은 형태를 프로미스 체이닝(Promise chaining) 이라고 부른다.


// (2)
p
 .then(
   v => {
		console.log(v);
   },
   err => {
	  console.log(err);
   },
 );

아까 (1)-1의 설명에서 then 핸들러는 두 개의 콜백함수 인자를 가진다고 했다.

에디터의 자동완성에 의하면 첫번째 콜백함수는 onFulfilled , 두번째 콜백함수는 onRejected 라는 이름을 가진다.

두번째 콜백함수 onRejected는 프로미스가 거부되었을 때 실행되는 녀석이다.

onFulfilled 에서 에러가 난다면 onRejected에서 처리를 하지 못하고 가까운 에러 핸들러를 찾는다.

아래의 3번예시를 보자.


// (3)
p
 .then(v => {
	console.log(v)	
}).catch(err => {
	console.log(err);
}).finally(() => {
	console.log("finally!");
})

.then()과 .catch(), .finally()를 섞어서 사용한 국밥 프로미스다.

.then()에서 에러가 나더라도 .catch()에서 잡아줄 수 있다.

또한 .catch()는 .then(null, onRejected)와 같다.


에러 catch후 다시 throw

new Promise((resolve, reject) => {

  throw new Error("에러 발생!");

}).catch(function(error) { // (*)

  if (error instanceof URIError) {
    // 에러 처리
  } else {
    alert("처리할 수 없는 에러");

    throw error; // 에러 다시 던지기
  }

}).then(function() {
  /* 여기는 실행되지 않습니다. */
}).catch(error => { // (**)

  alert(`알 수 없는 에러가 발생함: ${error}`);
  // 반환값이 없음 => 실행이 계속됨

});

.catch() 같은, 거부상태가 된 promise를 해결하는 코드가 없다면 전역 에러를 생성하게 된다. 브라우저 환경에서 unhandledrejection 이벤트로 확인 가능하다.


Promise API

Promise.all([...promises])

여러 개의 프로미스를 iterable object(보통은 배열)로 집어넣고, 이 프로미스들이 전부 완료된 다음 특정 코드를 실행시키고 싶을 때 사용한다.

Promise.allSettled([...promises]

인자로 넣은 프로미스들중 하나라도 거절되면 전체를 거절한다.

Promise.race([...promises])

인자로 넣은 프로미스들중 가장 먼저 처리된 프로미스의 결과(혹은 에러)를 반환한다.

Promise.any([...promises])

race와 유사한 동작을 보인다. 차이점은 인자로 넣은 프로미스들중 가장 먼저 ‘fulfilled’ 된 프로미스를 반환한다는 것이다.


Refer
YDKJS this와 객체 프로토타입, 비동기와 성능
javascript Info

profile
What is to give light must endure burning

0개의 댓글