

Promise는 자바스크립트에서 비동기 작업을 처리하는 객체입니다.Promise는 어떤 작업이 완료되거나 실패했을 때 실행할 콜백 함수를 정의할 수 있는 객체입니다.Promise는 "약속"입니다. 무언가를 약속하고 그 약속을 지키면 뭔가를 실행하거나, 지키지 못하면 다른 일을 할 수 있게 도와주는 것입니다.pending)되면, 요리사는 주문 성공(resolve) 또는 주문 실패(reject)라는 약속을 합니다.resolve)하면, 주문한 음식을 받아서 먹을 수 있게 됩니다.reject)하면, 요리사가 다른 음식을 만들어 알려주면서 어떤 문제가 있었는지 설명해줄 수 있습니다.function addNumbers(num1, num2, callback) {
setTimeout(() => {
const result = num1 + num2;
callback(result);
}, 1000);
}
addNumbers(2, 3, (n1) => {
addNumbers(n1, 5, (n2) => {
addNumbers(n2, 5, (n3) => {
addNumbers(n3, 5, (n4) => {
addNumbers(n4, 5, (n5) => {
console.log("The final number is: " + n5);
});
});
});
});
});
콜백 함수가 중첩되면서 코드의 깊이가 깊어지면 코드의 가독성을 떨어뜨리고 코드의 흐름을 파악하기 어렵습니다. 또한 콜백 함수마다 에러처리를 따로 해줘야하며 에러가 발생한 위치를 찾기 어렵습니다.
function addNumbers(num1, num2) {
return new Promise((resolve, reject) => {
setTimeout(() => {
const result = num1 + num2;
resolve(result);
}, 1000);
});
}
addNumbers(2, 3)
.then((n1) => addNumbers(5, n1))
.then((n2) => addNumbers(5, n2))
.then((n3) => addNumbers(5, n3))
.then((n4) => addNumbers(5, n4))
.then((n5) => console.log("The final number is: " + n5));
비동기적으로 처리해야 하는 일이 많아질수록, 코드의 깊이가 계속 깊어지는 현상이 있는데, Promise를 사용하면 코드의 깊이가 깊어지는 현상을 방지할 수 있습니다.
const myPromise = new Promise((resolve, reject) => {
// executor (비동기 작업 수행)
const data = fetch('서버로부터 요청할 URL');
if(data)
resolve(data); // 만일 요청이 성공하여 데이터가 있다면
else
reject("Error"); // 만일 요청이 실패하여 데이터가 없다면
})
executor는 새로운 Promise 객체가 생성될 때 자동으로 실행되는 함수입니다. new 키워드와 Promise 생성자 함수를 사용하여 생성할 수 있습니다. executor 함수는 resolve와 reject라는 두 개의 매개변수를 가지며, 비동기 작업을 수행한 후 작업이 성공한 경우 resolve 함수를 호출하고, 작업이 실패한 경우 reject 함수를 호출합니다.
function fetchData() {
return new Promise((resolve, reject) => {
// 비동기 작업 수행
// 데이터를 성공적으로 가져오면 resolve 호출
// 에러가 발생하면 reject 호출
setTimeout(() => {
const data = "This is the fetched data";
if (data) {
resolve(data); // 데이터를 성공적으로 가져옴
} else {
reject("Failed to fetch data"); // 데이터 가져오기 실패
}
}, 2000);
});
}
fetchData() // fetchData 함수를 호출하고 프로미스를 처리합니다.
.then((data) => { // 성공적으로 수행했을 때 실행될 코드
console.log("Data:", data); // resolve(data)의 data값이 출력된다
})
.catch((error) => { // 실패했을 때 실행될 코드
console.error("Error:", error); // reject("Failed to fetch data")가 출력된다
});
위 코드는 Promise를 반환합니다. 비동기 작업이 완료된 이후 작업 결과에 따라 then()과 catch()를 사용하여 성공과 실패에 대한 후속 처리를 진행할 수 있습니다. 주어진 코드에서 data가 존재하는 경우, resolve(data)를 호출하고, 그 후 .then()을 연결하여 콜백 함수에서 성공에 대한 추가 처리를 수행합니다. resolve() 함수의 매개변수 값은 then 메서드의 콜백 함수 인자로 전달되어, 프로미스 객체 내부에서 다룬 값을 사용할 수 있게 됩니다. 이 결과로 "This is the fetched data" 가 출력됩니다. 만약 처리가 실패하여 reject()를 호출하게 되면, .catch()로 이어져 catch메서드의 콜백 함수에서 실패에 대한 추가 처리를 진행하며 "Failed to fetch data" 를 출력합니다.
프로미스 객체를 변수로 할당하는 방식은 사용할 수 있지만, 보통의 경우 함수로 감싸서 사용하는 것이 더 일반적입니다.
const Promise1 = () => {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
console.log("This is the myPromise1");
}, 1000);
});
};
const Promise2 = new Promise((resolve) => {
setTimeout(() => {
resolve();
console.log("This is the myPromise2");
}, 1000);
});
위 코드에서 Promise1은 프로미스 객체를 반환하도록 구현했으며, Promise2는 프로미스 객체를 할당했습니다. executor는 새로운 Promise 객체가 생성될 때 자동으로 실행되는 함수입니다. 이 말은 Promise2는 즉시 실행되지만, Promise1은 호출하기 전까지 실행되지 않는다는 것을 의미합니다. 따라서 함수로 만들고 그 함수를 호출하면 프로미스 객체를 반환하여 생성된 프로미스 객체를 함수의 반환값으로 사용할 수 있습니다. 원하는 시점에 호출하거나 재사용하고 싶은 경우, Promise1과 같이 프로미스 객체를 반환하는 함수를 만들어야 합니다.
"동적으로 비동기 작업을 수행한다" 이 말은 함수
fetchData에 동적으로 전달된URL에 대한 비동기 데이터를 가져오는 작업을 수행한다는 내용입니다.
const fetchData = (url) => {
return new Promise((resolve, reject) => {
// 동적으로 전달된 url을 이용하여 비동기 작업을 수행
setTimeout(() => {
const data = "Data fetched successfully from " + url;
if (data) {
resolve(data);
} else {
reject(new Error("Failed to fetch data from " + url));
}
}, 1000);
});
};
const processData = (data) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
const processedData = "Processed data: " + data;
if (processedData) {
resolve(processedData);
} else {
reject(new Error("Failed to process data"));
}
}, 1000);
});
};
fetchData(`https://jsonplaceholder.typicode.com/posts/1`)
.then((data) => processData(data))
.then((processedData) => {
console.log("Final result:", processedData);
})
.catch((error) => {
console.error("Error:", error);
});
위 코드는 fetchData와 processData라는 두 개의 함수로 구성된 프로미스입니다. fetchData 함수는 주어진 URL에서 데이터를 가져오는 프로미스를 반환하고, processData 함수는 데이터를 가공하는 프로미스를 반환합니다. 이렇게 프로미스를 사용하면 fetchData와 processData 함수를 재사용할 수 있습니다. 예를 들어 다른 API 엔드포인트에서 데이터를 가져와 처리해야 하는 경우에도 같은 로직을 사용할 수 있습니다. 이런 재사용성은 코드의 유지 보수성을 높이고, 중복을 최소화하며 개발 생산성을 향상시킬 수 있습니다.
API 엔드포인트
API 엔드포인트는 웹 서비스 또는 웹 애플리케이션에서 외부에 제공되는 특정 URI 또는 URL을 의미합니다. 클라이언트가 서버와 통신할 때 해당 리소스에 접근하는 데 사용되는 URL 주소가 API 엔드포인트입니다."다른 API 엔드포인트에서 데이터를 가져와 처리"라는 문장은 다른 서버 또는 다른 리소스에서 데이터를 가져와서 그 데이터를 처리하는 것을 의미합니다. 위 코드에서는
fetchData함수가 이러한 엔드포인트를 사용하여 데이터를 가져오고, 이후processData함수에서 해당 데이터를 처리합니다.
Promise는 처리과정을 의미하는 3가지 상태(state)를 가지고 있습니다. Promise를 생성하고 종료될 때까지의 3가지 상태를 갖습니다. new Promise생성자가 반환하는 Promise객체는 다음과 같은 내부 프로퍼티를 갖습니다.
Pending(대기) : 프로미스가 아직 처리되지 않은 상태입니다.Fulfilled(이행) : 프로미스가 성공적으로 처리되어 결과 값을 반환한 상태입니다.Rejected(거부) : 프로미스가 처리 중에 오류가 발생하여 처리를 실패한 상태입니다.
Pending 상태는 프로미스 객체가 생성되고, 비동기 작업이 아직 완료되지 않은 초기 상태를 나타냅니다. 프로미스 객체를 생성한 후 콘솔로 확인해보면 프로미스 객체의 상태가 Pending으로 출력됩니다.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("처리 완료");
}, 5000);
});
console.log(myPromise); // "pending"


위에서 작성한 코드를 실행한 후 5초 뒤에 콘솔로 다시 확인하면 Fulfilled 상태로 변하게 됩니다. 5초가 지나면 resolve("처리 완료")가 실행되어 프로미스의 성공을 알리는 개체를 호출하고, 비동기 로직이 성공적으로 완료되었음을 나타내는 상태를 보여줍니다.

그리고 Fulfilled 상태로 변한 프로미스는 .then()을 연결하여 성공에 대한 추가 처리를 수행할 수 있습니다.
myPromise.then((data) => {
console.log("프로미스 처리 완료!!!");
});
비동기 작업이 실패하거나 에러가 발생한 상태입니다. reject("처리 실패")를 호출하면 프로미스 객체가 Rejected상태가 됩니다. 5초뒤 콘솔로 확인해보면 Rejected가 출력되는걸 확인할 수 있습니다.
const myPromise = new Promise((resolve, reject) => {
setTimeout(() => {
reject("처리 실패");
}, 5000);
});
console.log(myPromise);


Rejected 상태로 변한 프로미스는 .catch()을 연결하여 실패에 대한 추가 처리를 진행할 수 있습니다.
myPromise.catch((error) => {
console.log(error);
console.log("프로미스 처리 실패!!!");
});
프로미스의 체이닝은 여러 개의 Promise를 연결하여 비동기 작업을 순차적으로 처리하는 방식입니다. 아래 코드는 순차적으로 다섯 번의 비동기적인 숫자 덧셈 작업을 수행합니다. 각 작업은 이전 작업의 결과값을 기반으로 차례로 실행되며, 마지막에 최종 결과값이 콘솔에 출력됩니다.
function addNumbers(num1, num2) {
return new Promise((resolve) => {
setTimeout(() => {
const result = num1 + num2;
resolve(result);
}, 1000);
});
}
addNumbers(2, 3)
.then((n1) => addNumbers(5, n1))
.then((n2) => addNumbers(5, n2))
.then((n3) => addNumbers(5, n3))
.then((n4) => addNumbers(5, n4))
.then((n5) => console.log("The final number is: " + n5));

프로미스에는 여러 정적(static)메소드가 있습니다. 이러한 정적 메소드들은 주로 프로미스의 생성과 관련된 작업을 수행하거나, 여러 프로미스를 조작하고 처리하는 데 사용됩니다.
Promise.all()은 여러 개의 프로미스를 동시에 실행하고, 모든 프로미스가 완료될 때까지 기다렸다가 그 결과를 배열로 반환하는 메소드입니다.
const promise1 = new Promise((resolve) => {
setTimeout(() => {
resolve('Promise 1 resolved');
}, 2000);
});
const promise2 = new Promise((resolve) => {
setTimeout(() => {
resolve('Promise 2 resolved');
}, 3000);
});
const promise3 = new Promise((resolve) => {
setTimeout(() => {
resolve('Promise 3 resolved');
}, 1500);
});
Promise.all([promise1, promise2, promise3])
.then((results) => {
console.log(results);
})
.catch((error) => {
console.error(error);
});
위 코드에서 Promise.all()은 [promise1, promise2, promise3] 배열의 프로미스들을 동시에 실행합니다. 모든 프로미스가 완료되면, .then() 메소드가 실행되고, 각 프로미스의 결과가 results 배열로 전달됩니다. 만약 하나라도 프로미스가 거부된다면 .catch() 블록이 실행됩니다.

Promise.race()메소드는 입력된 프로미스들 중에서 가장먼저 fulfilled(이행)되거나 rejected(거부)된 프로미스를 기반으로 새로운 프로미스를 반환합니다.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 1');
}, 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 2');
}, 1000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 3');
}, 3000);
});
const promises = [promise1, promise2, promise3];
Promise.race(promises)
.then(result => {
console.log(result); // 'Promise 2'
})
.catch(error => {
console.error(error);
});
Promise.race()는 fulfilled(이행) 또는 rejected(실패) 여부와 관계없이 가장 먼저 처리가 끝난 Promise2를 콘솔에 출력합니다.

Promise.all()은 주어진 모든 프로미스가 모두 완료되어야만 결과를 도출하는 반면, Promise.any()는 주어진 모든 프로미스 중 하나라도 완료(fulfilled)되면 즉시 반환하는 정적 메서드입니다.
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 1');
}, 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 2');
}, 1000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 3');
}, 3000);
});
const promises = [promise1, promise2, promise3];
Promise.any(promises)
.then(result => {
console.log(result); // 'Promise 3'
})
.catch(errors => {
console.error(errors); // ['Promise 1', 'Promise 2']
});
예제 코드를 보면 Promise3이 출력되는 것을 확인할 수 있습니다. Promise.any()는 여러 프로미스 중에서 가장 먼저 이행(fulfilled)된 프로미스의 결과를 반환하기 때문에 거부(rejected) 상태인 Promise1과 Promise2는 무시됩니다.

const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 1');
}, 2000);
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('Promise 2');
}, 1000);
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Promise 3');
}, 3000);
});
const promises = [promise1, promise2, promise4];
Promise.any(promises)
.then(result => {
console.log(result);
})
.catch(errors => {
console.error(errors); // AggregateError: All promises were rejected
console.error(errors.errors); // ['Promise 1', 'Promise 2', 'promise4 failed']
});
Promise.any()를 사용하여 여러 프로미스를 처리하고, 결과에서 거부(rejected)된 프로미스 에러들을 AggregateError로 묶어서 처리합니다.

AggregateError
AggregateError는 여러 에러를 하나의 에러로 묶어주는 객체로, ES2021(ECMAScript2021)에서 도입된 새로운 에러 타입 중 하나입니다. AggregateError는 여러 에러가 발생한 상황에서 모든 에러를 캡슐화 하여 하나의 AggregateError로 처리할 수 있으며, 여러 에러를 한 번에 사용자에게 전달할 때 유용합니다. 이 배열은errors라는 속성을 통해 접근할 수 있습니다.
Promise.resolve()는 주어진 값을 가지는 이행(resolve) 상태의 프로미스를 생성하는 메소드입니다. Promise.resolve()는 두 가지 경우를 다룰 수 있습니다.
const value = 42;
const promise = Promise.resolve(value);
promise.then((result) => {
console.log(result); // 42
});
const existingPromise = new Promise((resolve) => resolve('Hello, Promise!'));
const resolvedPromise = Promise.resolve(existingPromise);
resolvedPromise.then((result) => {
console.log(result); // 'Hello, Promise!'
});
이러한 특징은 특히 함수에서 비동기 작업의 결과를 반환하거나 이미 완료된 작업을 프로미스로 감싸는 등의 상황에서 사용됩니다.
Promise.reject(reason)는 주어진 이유(reason)로 거부된(rejected) 프로미스를 반환합니다.
const errorReason = new Error('Something went wrong');
const rejectedPromise = Promise.reject(errorReason);
rejectedPromise.catch((error) => {
console.error('Error:', error.message); // 'Something went wrong'
});
Promise.reject(reason)는 거부된 상태의 프로미스를 생성하며, catch 메소드를 사용하여 거부된 이유를 처리합니다. 여기서 reason은 거부된 프로미스의 이유를 설명하는 값입니다. 또한, reason은 선택적인 매개변수이며, 생략될 경우 기본값은 undefined입니다.
const rejectedPromise = Promise.reject();
rejectedPromise.catch((reason) => {
console.error('Reason:', reason); // undefined
});
