5 minute read


Promise 란?

Promise ?
ES6에서 자바스크립트의 콜백 패턴이 가진 단점을 보완하며 비동기 처리 시점을 명확하게 표현하기 위해 도입한 패턴

Promise 객체는 수행결과가 나오기 이전인 Pending, 성공 fulfilled, 실패 rejected 3가지 상태 중 하나를 갖는다.

상태 설명
pending(대기) 수행결과가 나오기 이전의 초기 상태
fulfilled(성공) 연산이 성공적으로 실행
rejected(실패) 연산이 실패


resolve 함수가 리턴 되면 state가 fulfilled(성공)으로 변경되고 ‘value’가 resolve 함수에 전달되며, reject 함수가 리턴되면 state가 rejected(실패))로 변경되고 value가 reject 함수에 전달된다.



resolve와 reject 2개의 파라미터를 갖고 성공한경우 resolve, 실패한경우 reject를 실행한다. 이 때 리턴 값 설정이 가능하다

한 번이라도 성공 또는 실패로 처리가 완료된 Promise는 초기로 돌아가지 않는다.

1. Promise 기본 사용법

Promise는 resolve와 reject 2개의 파라미터를 갖고 성공한경우 resolve, 실패한경우 reject를 실행한다.

아래 코드를 통해 간단히 확인해보자.

1
2
3
4
5
6
7
8
9
//then 호출(성공시)
var prm = new Promise((resolve, reject) => {
	resolve(10);
});

//catch 호출(실패시)
var prm = new Promise((resolve, reject) => {
	reject(5);
});

첫 번째 Promise에서 성공시에 호출되는 resolve를 강제적으로 호출하도록 작성하고 한뒤 아래 코드를 실행하면 then의 console이 출력된다.
마찬가지로 두 번째 Promise에서 실패시 호출되는 reject를 강제적으로 호출하돌고 작성한 뒤 아래 코드를 실행하면 catch의 console이 출력되는 것을 볼 수 있다.

1
2
3
4
5
6
7
8
prm.then(result => {
		console.log(`resolve : ${result}`);
		//result : ressolve : 10
	})
	.catch(error => {
		console.log(`catch : ${error}`);
		//result : catch : 5
	});

하나 알아둬야할 Promise의 특징은 then 이 호출되었을 때는 catch가 호출되지 않으며, 반대로 catch가 호출되었을 때는 then이 호출되지 않고 catch는 내부 로직 중 어느 파트에서든 에러가 발생되면 바로 호출되어진다는 점이다.


2. Promise chaining

then() 메서드는 값이 값으로 확인된 새로운 Promise를 반환하므로 새롭게 반환되는 Promise에서 then() 메서드를 호출할 수 있는데 이러한 특징을 Promise Chaining이라고 한다. 조금 쉽게 말해보자면 then() 메서드에서 return을 사용하면 결과 값이 기본 자료형이 아닌 Promise 객체를 반환한다는 뜻이다. 이 특징을 이용해 체인형식의 구조로 코드를 작성할 수 있다.

2.1 사용방법

아래 코드에서 then() 메서드에 return을 사용하여 다음 then() 메서드를 호출하여 result에 10씩 더해지는 것을 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
let prm = new Promise((resolve, reject) => {
	resolve(10);
});

prm.then((result) => {
		console.log(`1st result: ${result}`);
		// 새로운 Promise 객체 반환으로 2번째 then 메서드를 호출
		return result + 10;     
	})
	.then((result) => {
		console.log(`2nd result: ${result}`);
		return result + 10;
	})
	.then((result) => {
		console.log(`3rd result: ${result}`);
	});

	// result : 10, 20, 30


하지만 Promise를 독립적으로 여러번 실행하는 경우는 체인 구조를 사용할 수 없다.
아래의 구조대로 실행시킨다면 모든 결과는 10으로 동일하게 나오는 것을 확인할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
prm.then((result) => {
	console.log(`1st result: ${result}`);
	return result + 10;
});

prm.then((result) => {
	console.log(`2st result: ${result}`);
	return result + 10;
});

prm.then((result) => {
	console.log(`3st result: ${result}`);
	return result + 10;
});

// result : 10, 10, 10


또한 아래 코드와 같이 retrun에 선언해둔 Promise를 호출하여 반환할 수도 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
function gotoWork(num){
	return new Promise((resolve, reject) => {
		if(num == 1){
			resolve("출근합니다.");
		}else{
			reject();
		}
	});
}

function working(num){
	return new Promise((resolve, reject) => {
		if(num == 1){
			resolve("일을 합니다.");
		}else{
			reject();
		}
		
	});
}

function leaveWork(){
	return new Promise((resolve, reject) => {
		resolve("퇴근합니다.");
	});
}

function vacation(){
	return new Promise((resolve, reject) => {
		resolve("오늘은 휴가입니다.");
	});
}

gotoWork(1)
	.then(result => {
		return working(2);
	})
	.then(result => {
		return leaveWork();
	})
	.then(result => {
	})
	.catch(() => {
		vacation();
	});


2.2 축약 코드

아래는 Promise의 기본 코드를 축약해서 사용 하는 방법이다. 간단한 샘플을 통해 알아보자.

1
2
3
4
5
6
7
8
9
10
11
step1()
	.then(result => step2(result))
	.then(result => step3(result))
	...

//위 아래는 동일 코드

step1()
	.then(step2)
	.then(step3)
	...


step1을 호출하고 실행이 완료되면 Promise Chaining 규칙에 의해 step1의 결과를 step2의 인자로 받고 step2가 실행된다. 동일한 원리로 step3가 실행되는 구조인데 호출하는 부분을 보면 .then() 내부의 호출부분이 단순 함수명으로 이루어진 것을 볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
function step1(name) {
	return new Promise((resolve, reject) => {
		console.log('step1.');
		setTimeout(() => {
			resolve({
				userName : name
			});
		}, 1000);
	})
}

function step2(user) {
	return new Promise((resolve, reject) => {
		console.log(`step2. result : ${user.userName}`);
		setTimeout(() => {
			resolve({age:31, tall:182});
		}, 1000);
	});
}

function step3(data) {
	return new Promise((resolve, reject) => {
		console.log(`step3. result : ${JSON.stringify(data)}`);
		setTimeout(() => {
			resolve(data);
		}, 1000);
	});
}


1
2
3
4
5
//호출
step1('Son')
	.then(step2)
	.then(step3)
	.then(console.log);



3. Promise.all

Promise.all은 모든 Promise가 실행되고 결과를 리턴할 때까지 기다렸다가 Promise의 모든 결과를 배열로 리턴해주는 메서드이다.

모든 Promise가 실행되고 성공해야 하는경우에 사용하며 하나라도 실패하면 다른 Promise가 성공했더라도 Promise.all은 실패로 떨어진다. 아래 예제처럼 timeOut을 통해 시간의 차이를 두었지만 먼저 종료가 된다고 해서 Promise.all에서 받는 인자의 순서가 달라지지 않고 선언된 순서대로 값을 받아온다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const promise1 = new Promise((resolve, reject) => {
	setTimeout(resolve, 3000, 'data1');
});

const promise2 = new Promise((resolve, reject) => {
	setTimeout(resolve, 1000, 'data2');
});

const promise3 = new Promise((resolve, reject) => {
	setTimeout(resolve, 2000, 'data3');
});

Promise.all([promise1, promise2, promise3]).then((values) => {
  console.log(values);
});

//result : ['data1', 'data2', 'data3']



4. Promise.allSettled

Promise.allSettled는 Promise.all과 실행방법은 동일하나 결과가 다르다. 실행 결과가 하나만 실패해도 전체 실패가 떨어지는 all과 달리 allSettled는 성공과 실패 여부를 그대로 반환한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
const promise1 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('data1');
  }, 1000);
});
 
const promise2 = new Promise((resolve) => {
  setTimeout(() => {
    resolve('data2');
  }, 2000);
});
 
const promise3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject(new Error('Error!!'));
  }, 1500);
});
 
Promise.allSettled([promise1, promise2, promise3])
  .then((result) => console.log(result))
  .catch((e) => console.error(e));

// result
[
	{status: 'fulfilled', value:'data1'}
	{status: 'fulfilled', value:'data2'}
	{status: 'rejected', value:'Error!!'}
]