[Javascript] Promise 객체

Bam·2022년 2월 28일
0

Javascript

목록 보기
77/106
post-thumbnail

비동기 처리

우선 비동기 처리에 대해 짧게 이야기하고 넘어가겠습니다. 자세한 이야기는 이 포스트를 참조해주세요.

비동기 처리는 데이터의 처리를 서버단에서 수행하고, 클라이언트측(브라우저)에서는 사용자와의 상호작용만을 하는 처리 방식입니다. 이 방식덕에 사용자는 요청 후 응답까지 기다리지 않아도 되고, 새로고침(리프레쉬)같은 동작이 없다는 것이 큰 장점입니다. 그래서 현대의 웹사이트에서는 비동기 처리방식이 대세를 이루게 되었고 자바스크립트는 비동기 처리를 위한 다양한 객체들을 지원하고 있습니다. 그 중 가장 많이 사용되는 방식이 오늘 다루는 Promise 객체입니다.

콜백 함수

콜백 함수가 사실은 여러번 등장했던 단어인데, 다시 한 번 더 짚고 넘어가겠습니다. 콜백 함수(Callback function)는 특정 시점에서 호출되는 함수입니다. 명시적으로 호출되는 일반 함수와는 다르게 등록되어 있다가 특정 조건(시점)을 만족하면 호출이됩니다.

그동안 자바스크립트에서는 비동기 처리를 위해서 콜백 함수를 사용했었습니다. 하지만, 콜백 함수는 비동기 처리가 중첩될 때마다 코드나 무척 복잡해지는 경향이 있었습니다.

1번 콜백 함수 {
	2번 콜백 함수 {
    		3번 콜백 함수 {
        		4번 콜백 함수 {
            			5번 콜백 함수 {
                }
            }
        }
    }
}

위와 같은 현상을 콜백 지옥이라고 부릅니다. 이 콜백 지옥을 해결하기 위해 등장한 대안이 바로 Promise 객체입니다.

Promise 객체

Promise 객체는 비동기 처리를 하는 객체입니다. 기본적으로 다음과 같은 구성을 갖습니다.

const promise = new Promise((resolve, reject) => {
	//처리 내용
});

프로미스 객체의 인수로는 함수를 받는데, 이 함수 내부에 비동기 처리 코드를 작성합니다. 그리고 이 함수의 인자인 resolvereject가 눈에 띄는데, resolve는 비동기 처리의 성공을 알리는, reject는 비동기 처리의 실패를 알리는 함수입니다. 결과에 따라 resolvereject를 Promise 객체가 알아서 전달해주기에, 크게 신경쓰지 않아도 됩니다.

then, catch

비동기 처리의 성공 결과인 resolve를 받는 것은 then 메소드입니다. 실패 결과인 rejectcatch 메소드가 받게됩니다. 비동기 콜백 작업 후에 실행하게 해주는 finally도 존재합니다.

아래 코드가 Promise 객체를 만들고 사용하는 코드입니다. Promise 객체에 비동기 처리 내용을 기술하고, 해당 결과에 따라서 then, catch 메소드를 실행합니다.

const promise = new Promise((resolve, reject)=>{
	//처리 내용
});

promise.then(
	//resolve가 호출되면 then이 실행
)
.catch(
	//reject가 호출되면 catch가 실행
)
.finally(
	//콜백 작업을 마치고 무조건 실행되는 finally (생략 가능)
)

한 번 구체적인 예제 코드로 Promise 객체를 이해해보겠습니다.

const flag = true;
const promise = new Promise(((resolve, reject) => {
    if (flag) {
        resolve('resolve가 되었음');
    }
    else {
        reject('reject가 되었음');
    }
}));

promise.then((resolveMessage) => {
    console.log(resolveMessage);
})
.catch((errorMessage) => {
	console.log(errorMessage);
});


논리형 변수 flag가 true면 resolve를 호출하고, false면 reject를 호출하도록 비동기 처리를 했는데, flag가 true이므로 then 메소드의 코드가 실행되었음을 볼 수 있습니다.

여러 비동기 처리 연결하기

then, catch 메소드에 또 다른 then, catch 메소드를 이어붙임으로써 여러 비동기 처리를 연결할 수 있습니다.

방금 전의 예제 코드를 변형시켜, 두 개의 비동기 처리를 서로 연결시켜보겠습니다.

const flag = true;
const promise = new Promise(((resolve, reject) => {
    if (flag) {
        resolve('resolve가 되었음');
    }
    else {
        reject('reject가 되었음');
    }
}));

promise.then((resolveMessage) => {
    console.log(resolveMessage);

    return new Promise(((resolve, reject) => {
        if (flag) {
            resolve('resolve가 되었음2');
        }
        else {
            reject('reject가 되었음2');
        }
    }));
})
.then((resolveMessage2)=> {
    console.log(resolveMessage2);
})
.catch((errorMessage) => {
    console.log(errorMessage);
});

처음으로 then 메소드를 실행하는 구문에서 return 명령으로 또 다른 Promise 객체를 반환합니다. 그 다음 뒤에 then 메소드를 더 이어서 반환된 Promise 객체에 대한 비동기 처리 결과를 실행합니다.

promise.then((resolveMessage) => {
    console.log(resolveMessage);

    return new Promise(((resolve, reject) => {
        if (flag) {
            resolve('resolve가 되었음2');
        }
        else {
            reject('reject가 되었음2');
        }
    }));
})
.then((resolveMessage2)=> {
    console.log(resolveMessage2);
})

Promise.all

방금 전에 본 방식은 스크립트가 위에서 아래로 실행되며 해석되기에 순차적으로 비동기 처리를 했습니다. all 메소드를 사용하면, 여러 비동기 처리를 병행적으로 수행할 수 있습니다.

Promise.all(
	//병행 처리할 Promise 객체들
)

메소드의 인수로는 대괄호[]를 사용해서 프로미스 객체들을 전달합니다. all메소드를 이용한 여러 개의 Promise 객체들을 병렬 처리하는 것을 예제로 보겠습니다.

const promise1 = new Promise((resolve, reject) => {
    resolve('resolve 되었음1');
});
const promise2 = new Promise((resolve, reject) => {
    resolve('resolve 되었음2');
});
const promise3 = new Promise((resolve, reject) => {
    resolve('resolve 되었음3');
});

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


지금은 모두 resolve를 반환하기에 then 메소드를 실행했습니다.
만약 all메소드로 전달된 Promise 객체들 중 하나라도 reject를 호출한다면, catch 메소드가 실행됩니다.

Promise.race

Promise.race {
  //프로미스 객체들
}

race메소드는 all 메소드처럼 여러 프로미스들을 전달하고, 그 중에서 하나가 먼저 완료됐을 경우 then 메소드를 호출합니다.

이전의 all 메소드를 race 메소드로 교체했을 때 무슨일이 일어나는지 보여드리겠습니다.

const promise1 = new Promise((resolve, reject) => {
    resolve('resolve 되었음1');
});
const promise2 = new Promise((resolve, reject) => {
    resolve('resolve 되었음2');
});
const promise3 = new Promise((resolve, reject) => {
    resolve('resolve 되었음3');
});

Promise.race([
    promise1,
    promise2,
    promise3,
])
.then((message) => {
    console.log(message);
})
.catch((errorMessage) => {
    console.log(errorMessage);
});


코드 순서상 먼저온 promise1이 실행되자, then을 실행한 모습입니다. 나머지 프로미스들은 성공했다고 하더라도 나타나지는 않습니다.

Promise.resolve, Promise.reject

Promise.resolve(value);
Promise.reject(reason);

Promise.resolvePromise.reject는 각각 resolve와 reject를 즉시 호출하는 메소드입니다. resolve는 then을 실행하며, 인수로 전달된 값을 함께 넘겨주고, reject는 인수로 주는 이유를 catch에 보냅니다.. 코드의 결과에 상관없이 이 두 메소드를 이용하면, 원하는 프로미스 결과를 얻을 수 있습니다.

const promise1 = Promise.resolve('resolve가 되었음');
const promise2 = Promise.reject('reject가 되었음');

promise1.then((resolveMessage)=>{
    console.log('resolve: ' + resolveMessage);
})
.catch((rejectMessage)=>{
    console.log('reject: ' + rejectMessage);
});

promise2.then((resolveMessage)=>{
    console.log('resolve: ' + resolveMessage);
})
.catch((rejectMessage)=>{
    console.log('reject: ' + rejectMessage);
});


참조

0개의 댓글