본문 바로가기
취준적인

Promise와 CallBack 차이

by klm hyeon woo 2023. 2. 9.

Promise와 CallBack의 차이

콜백 함수는 비동기 로직의 결과값을 처리하기 위해서 callback 안에서만 처리를 해야하고, 콜백 밖에서는 비동기에서 온 값을 알 수가 없다. 하지만, Promise를 사용하면 비동기에서 온 값이 프로미스 객체에 저장되기 때문에 코드 작성이 용이해진다.


Promise(프로미스)란

프로미스는 자바스크립트 비동기 처리에 사용되는 객체이다.

비동기 처리란 `특정 코드의 실행이 완료될 때까지 기다리지 않고 다음 코드를 먼저 수행하는 자바스크립트의 특성`이다.

 

비동기 처리 방식을 사용하는 이유는 2가지 핵심요소 때문이다.

- return value를 이용할 수 있다.

- error handling(에러 핸들링)이 동기식 코드와 유사하게 쓰일 수 있다.

 

프로미스를 사용할 때 알아야하는 가장 기본적인 개념은 바로 프로미스의 상태(states) 값이다.

여기서 말하는 상태란 프로미스의 처리 과정을 의미하며, new Promise()를 통해 프로미스를 생성하고 종료될 때까지 3가지 상태를 갖는다.

 

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

Pending, 대기 상태

아래와 같이 new Promise() 메서드를 호출하면 제일 처음 대기(Pending) 상태가 된다.

new Promise()

new Promise() 메서드를 호출할 때 콜백 함수를 선언할 수 있고, 콜백 함수의 인자는 resolve, reject가 된다.

new Promise(function(resolve, reject) {
	resolve();
});

resolve 안에 인자를 넣게 되면, 이행 상태가 되었을 때 resolve 안에 들어간 데이터가 실행이 되게 된다.

function getData() {
	return new Promise(function(resolve, reject) {
        var data = 100;
        resolve(data);
    });
}

// resolve()의 결과 값 data를 reseloveData라는 함수를 통해 받을 수 있다.
getData().then(function(resolvedData) {
	console.log(resolvedData);
});

// 평소 웹 개발할 때 사용하던 axios에서의 .then 처럼도 뽑아낼 수 있다.
getData().then((item) => {
    return (item)
    }
});

Rejected, 실패 상태

new Promise()로 프로미스 객체를 생성하면 콜백 함수 인자로 resolve와 reject를 사용할 수 있다고 했다.

여기서 reject를 아래와 같이 명시해주고, 괄호 안에 인자를 넣어주면 실패했을 때의 상태 값을 얻을 수 있다.

new Promise(function(resolve, reject) {
	reject();
});

실패 상태가 되면 실패한 이유(실패 처리의 결과 값)를 catch()를 통해 받을 수 있다.

function getData() {
	return new Promise(function(resolve, reject) {
    	reject(new Error("Request is failed"));
    });
};

// reject()의 결과 값 Error 객체를 err에 받아준다.
getData()
.then()
.catch(function(err) {
	console.log(err);
});

// Arrow로 이렇게 에러 처리를 할 수도 있다.

getData()
.then()
.catch((err) => {
	console.log(err)
});

프로미스 예제

보통은 axios를 통해 API를 호출받았었는데, 프로미스를 통해 API를 호출받게 되면

function getData() {
	return new Promise(function(resolve, reject) {
    	$.get('API 주소', function(res) {
        	if (res) {
            	resolve(res);
            }
            reject(new Error("Request is failed"));
    	});
    });
};

getData()
.then(function(data) {
	console.log(data);
})
.catch(function(err) {
	console.log(err);
});
.fianlly(function() { // finally 메소드는 Promise의 성공과 실패에 관계없이 실행되어지는 함수이다.
	console.log("finally !");
});

Promise.all 

promise.all 메소드는 배열과 같이 순회 가능한 객체(주로 거의 배열이라고 한다)를 인자로 받는다.

해당 배열 안의 프로미스가 모두 이행되면(배열 요소가 반드시 프로미스일 필요는 없다), 각각의 프로미스 결과 값을 담은 배열을 이행 결과 값으로 새로운 프로미스 객체를 반환한다.

const one = new Promise((resolve, reject) => {
    setTimeout(() => resolve("one"), 1000);
});
const two = new Promise((resolve, reject) => {
    setTimeout(() => resolve("two"), 2000);
});
const three = new Promise((resolve, reject) => {
    setTimeout(() => resolve("three"), 3000);
});

// one, two, three 함수에서 나온 프로미스 객체들을 3초 후에 배열 결과값으로 갖는 프로미스 객체로 반환한다.
Promise.all([one, two, three]).then((val) => console.log(val));


// 프로미스 객체가 아니어도, Promise.all을 사용 가능하다.
Promise.all(["one", "two", "three"])
    .then((res) => {
        console.log(res);
    })

하지만 배열에서, 배열의 요소 중 하나의 프로미스가 에러를 일으켜 거부되는 즉시, 다른 프로미스 이행 여부와 관계없이 해당 에러 및 거부 사유를 결과 값으로 반환한다. (다른 이행에 대한 값을 볼 수 없다)

const one = new Promise((resolve, reject) => {
    setTimeout(() => resolve("one"), 1000);
});
const two = new Promise((resolve, reject) => {
    setTimeout(() => resolve("two"), 2000);
});
const three = new Promise((resolve, reject) => {
    setTimeout(() => reject(new Error("Error!!")), 3000);
});

Promise.all([one, two, three])
    .then((val) => console.log(val))
    .catch((err) => console.log(err));
    // 다른 프로미스 이행 여부와 관계없이 catch 메소드가 호출
    // 반환하는 프로미스의 이행 값은 매개변수로 주어진 프로미스의 순서와 일치하며, 완료 순서에 영향을 받지 않는다.
  
const one = new Promise((resolve, reject) => {
  setTimeout(() => resolve("one"), 3000);
});
const two = new Promise((resolve, reject) => {
  setTimeout(() => resolve("two"), 2000);
});
const three = new Promise((resolve, reject) => {
  setTimeout(() => resolve("three"), 1000);
});

Promise.all([one, two, three]).then((val) => console.log(val));
// ["one", "two", "three"] 출력
// 배열의 첫번째 요소가 가장 마지막으로 이행 값을 반환했지만, 전달된 순서를 유지함.

위의 예시를 보면, 여러가지 비동기 작업을 병렬적으로 실행하는 과정에서 비동기 작업이 시작된 순서를 유지해야 되는 경우라면 Promise.all을 활용하면 된다. 


레퍼런스

 

프로미스(Promise)란? 콜백함수와의 차이점

"A promise is an object that may produce a single value some time in the future"프로미스는 자바스크립트 비동기 처리에 사용되는 객체이다. 비동기 처리란 '특정 코드의 실행이 완료될 때까지 기다리지 않고 다음

velog.io

 

댓글