[자바스크립트] Promise

이윤우·2023년 3월 21일
0

JavaScript

목록 보기
23/34
post-thumbnail

Promise 란?

Promise는 자바스크립트 비동기 처리에 사용되는 객체입니다. Promise 이전엔 비동기 처리를 위해 콜백 함수를 이용했습니다. 전통적인 콜백 패턴은 콜백 헬러 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한 번에 처리하는 데도 한계가 있었습니다.

이러한 한계를 극복하기 위해 ES6에서는 비동기 처리를 위한 프로미스를 도입했습니다.

// 콜백 헬
get('/step1', a => {
	get('/step2', b => {
    	get('/step3', c => {
        	get('/step4', d => {
            	console.log(d);
            })
        })
    })
})

// 에러 처리의 한계
try {
	setTimeout(() => { throw new Error('Error!'); }, 1000);
} catch (e) {
  	// 에러를 캐치하지 못한다
	console.error('캐치한 에러', e);
}

프로미스의 상태

프로미스는 현재 비동기 처리가 어떻게 진행되고 있는지를 나타내는 상태 정보를 갖습니다.

프로미스의 상태 정보의미상태 변경 조건
pending비동기 처리가 아직 수행되지 않은 상태프로미스가 생성된 직후 기본 상태
fulfilled비동기 처리가 수행된 상태(성공)resolve 함수 호출
rejected비동기 처리가 수행된 상태(실패)reject 함수 호출

생성된 직후의 프로미스는 기본적으로 pending 상태입니다. 이후 비동기 처리가 수행되면 비동기 처리 결과에 따라 다음과 같이 프로미스의 상태가 변경됩니다.

  • 비동기 처리 성공: resolve 함수를 호출해 프로미스를 fulfilled 상태로 변경한다.
  • 비동기 처리 실패: reject 함수를 호출해 프로미스를 rejected 상태로 변경한다.

fulfilled 또는 rejected 상태를 settled 상태라고 하는데 한 번 settled 상태가 되면 더는 다른 상태로 변화할 수 없습니다.

프로미스의 정적 메서드

1. Promise.all

Promise.all 메서드는 여러 개의 비동기 처리를 모두 병렬 처리할 때 사용합니다.

const request1 = () => new Promise(resolve => setTimeout(() => resolve(1), 1000));
const request2 = () => new Promise(resolve => setTimeout(() => resolve(2), 2000));
const request3 = () => new Promise(resolve => setTimeout(() => resolve(3), 3000));

const array = []
request1()
	.then(data => {
		array.push(data);
  		return request2();
	})
	.then(data => {
		array.push(data);
  		return request3();
	})
	.then(data => {
		array.push(data);
  		console.log(array); // [1, 2, 3] => 약 6초 소요
	})

// 3개의 비동기 처리를 동시에 병렬로 처리
Promise.all([request1(), request2(), request3()])
	.then(console.log) // [1, 2, 3] => 약 3초 소요

Promise.all 메서드는 프로미스를 요소로 갖는 배열등의 이터러블을 인수로 받습니다. 그리고 전달받은 모든 프로미스가 모두 fulfilled 상태가 되면 모든 처리 결과를 배열에 저장해 새로운 프로미스를 반환합니다.

만약 전달받은 배열의 프로미스가 하나라도 rejected 상태가 되면 나머지 프로미스가 fulfilled 상태가 되는 것을 기다리지 않고 즉시 종료합니다.

2. Promise.race

Promise.race 메서드는 프로미스를 요소로 갖는 이터러블을 인수로 받습니다. Promise.all 메서드처럼 모든 프로미스가 fulfilled 상태가 되는 것을 기다리는 것이 아니라 가장 먼저 fulfilled 상태가 된 프로미스의 처리 결과를 resolve 하는 새로운 프로미스를 반환합니다.

const request1 = () => new Promise(resolve => setTimeout(() => resolve(1), 1000));
const request2 = () => new Promise(resolve => setTimeout(() => resolve(2), 2000));
const request3 = () => new Promise(resolve => setTimeout(() => resolve(3), 3000));

Promise.race([request1, request2, request3])
	.then(console.log) // 1

3. Promise.allSettled

Promise.allSettled는 프로미스를 요소로 갖는 이터러블을 인수로 받습니다. 전달받은 프로미스가 모두 settled 상태가 되면 처리 결과를 배열로 반환합니다.

Promise.allSettled([
	new Promise(resolve => setTimeout(() => resolve(1), 2000)),
  	new Promise((_, reject) => setTimeout(() => reject(new Error('Error')), 1000))
]).then(console.log);
/*
[
	{status: "fulfilled", value: 1},
    {status: "rejected", reason: Error: Error! at <anoymous>:3:54}
]
*/

마이크로태스크 큐

setTimeout(() => console.log(1), 0);

Promise.resolve()
	.then(() => console.log(2))
	.then(() => console.log(3));

프로미스의 후속 처리 메서드도 비동기로 동작하므로 1 -> 2 -> 3 순으로 출력될 것처럼 보이지만 2 -> 3 -> 1의 순으로 출력됩니다. 그 이유는 프로미스의 후속 처리 메서드의 콜백 함수는 태스크 큐가 아니라 마이크로태스크 큐에 저장되기 때문입니다.
콜백 함수나 이벤트 핸들러를 일시 저장한다는 점에선 태스크 큐와 비슷하지만 마이크로태스크 큐는 태스크 큐보다 우선순위가 높기 때문에 이벤트 루프는 콜 스택이 비면 먼저 마이크로태스크 큐에서 대기하고 있는 함수를 가져와 실행합니다.

0개의 댓글