JavaScript - Promise(프로미스)

 

Promise란?

프로미스는 JavaScript에서 비동기 처리에 사용되는 객체이다.
비동기 처리란 ‘특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성’을 의미한다.

문제1. 비동기적인 일이 끝나기도 전에 일이 끝나고 실행 되어야하는 코드가 미리 처리가 돼서 문제가 생긴다.
문제2. 비동기로 병렬로 처리하게 되면 여러 개의 콜백 함수가 중첩되어 복잡도가 높아진다.
그렇게 되면 코드의 가독성이 좋지 않아 에러가 나기 쉽다.

이러한 문제점을 해결하기 위해 Promise를 사용한다. ES6부터 JavaScript의 내장객체로 추가되었다.

Promise의 3가지 상태

  • Pending : 비동기 처리 로직이 아직 완료되지 않은 상태 (대기)
  • Fulfilled : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태 (이행)
  • Rejected : 비동기 처리가 실패하거나 오류가 발생한 상태 (거부)

Pending(대기)

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

생성자를 통하여 프로미스 객체를 만드는 순간 대기(Pending) 상태가 된다.
프로미스 객체를 호출할 때 excutor라는 함수를 이용한다. excutor함수는 resolve,reject를 인자로 가진다.

Fulfilled(이행)

new Promise((resolve, reject) => {
  // 비동기적 코드
  resolve();
});

resolve함수를 실행하면, 이행(Fulfilled) 상태가 된다.

const p = new Promise((resolve,reject) => {
	// pending
	setTimeout( () => {
		resolve() // Fulfilled
	},1000);
});

p.then( () => { /* callback */ });

p라는 프로미스 객체가 Fulfilled 되는 시점에 p.then안에 설정된 callback 함수가 실행된다.

함수 P()로 실행

function p(){
	return new Promise((resolve,reject) => {
	// pending
	setTimeout( () => {
		resolve('성공') // Fulfilled
	},1000);
});

p().then( (message => {
	console.log("성공인가요?", message); // 성공인가요? 성공
});

함수로 실행하면 then을 실행하는 시점을 정확히 하고, 함수의 실행과 동시에 프로미스 객체를 만들면서 pending이 시작하도록 하기 위해서이다.
프로미스 객체를 생성하면서 리턴하는 p()를 만들어 함수p() 실행과 동시에 then을 설정한다.
대규모 프로젝트에서는 변수와 함수가 차이가 난다. 함수방법 사용을 권장한다.

resolve('성공') 함수를 실행할 때 인자를 넣어 실행하면, then의 callback함수의 인자message로 받을 수 있다.

Rejected(거부)

new Promise((resolve, reject) => {
  // 비동기적 코드
  reject();
});

reject함수를 실행하면, 거부(Rejected) 상태가 된다.

function p(){
	return new Promise((resolve,reject) => {
	// pending
	setTimeout( () => {
		reject(new Error('bad')) // Rejected
	},1000);
});

p().then( () => {
	console.log("성공");
})
.catch(error  => {
    console.log("실패인가요?", error); // 실패인가요? (에러출력)
});

rejected되는 시점에 p.catch안에 설정한 callback함수가 실행된다.
reject(new Error('bad')) 함수를 실행할 때 인자를 넣어 실행하면, then의 callback함수의 인자error로 받을 수 있다.
인자로는 주로 에러 객체를 사용한다.

Promise의 finally()

p().then(message => {
	console.log("성공인가요?",message); // Fulfilled
})
.catch(error => {
	console.log("실패인가요?", error); // Rejected
})
.finally() => {
	console.log("end");
});

finally()는 Fulfilled 되거나 Rejected 된 후에 결과에 상관없이 최종적으로 한 번만 실행되는 메소드이다.

Promise Chaining (프로미스 체이닝)

function p(){
	return new Promise((resolve,reject) => {
	// pending
	setTimeout( () => {
		resolve(1);
    }, 2000);
});

p().then(function(result) {
  console.log(result); // 1
  return result + 10;
})
.then(function(result) {
  console.log(result); // 11
  return result + 20;
})
.then(function(result) {
  console.log(result); // 31
});

then 함수에서 다시 프로미스 객체를 리턴하는 방법으로 여러 개의 프로미스를 연결하여 사용할 수 있다.
이 방식을 프로미스 체이닝이라고 한다.

위 코드는 프로미스 객체를 하나 생성하고 setTimeout()을 이용해 2초 후에 resolve()를 호출하는 예제이다.
result값을 더하여 다음 프로미스에게 연결되는 것을 볼 수 있다.

Promise.all([프로미스 객체들]);

function p(ms){
	return new Promise(resolve,reject) => {
		setTimeout( () => }
			resolve(ms);
		},ms);
	});
}

promise.all([p(1000),p(2000),p(3000)].then(message => {
	console.log('실행',message); // [1000,2000,3000]
});

프로미스 객체 여러 개를 생성하여, 배열로 만들어 인자를 넣고 Promise.all을 실행하면
모든 객체들이 Fulfilled 되었을 때, then함수를 실행한다.
then함수의 인자로 프로미스 객체들의 resolve 인자값을 배열로 돌려준다.

Promise.race([프로미스 객체들]);

function p(ms){
	return new Promise(resolve,reject) => {
		setTimeout( () => }
			resolve(ms);
		},ms);
	});
}

promise.race([p(1000),p(2000),p(3000)].then(message => {
	console.log('실행',message); // 1000
});

프로미스 객체 여러 개를 생성하여, 배열로 만들어 인자를 넣고 Promise.race을 실행하면
배열의 모든 프로미스 객체 중 가장 먼저 Fulfilled된 것으로, then의 함수가 실행된다.
then의 함수의 인자로 가장 먼저 Fulfilled된 객체의 resolve 인자값을 돌려준다.


참고자료 : https://joshua1988.github.io/web-development/javascript/promise-for-beginners/