콜백 함수를 사용하는 이유는 데이터를 받아오는 동안 다른 처리를 실행하고 받아온 데이터를 이후에 사용하기 위함이다.
// 비동기 함수 대체
setTimeout(() => {
// result를 특정한 비동기처리 이후에 받아온 값이라고 가정
const result = 10;
}, 1000);
위와 같은 비동기 함수를 실행할 때 해당 함수 내부에서 결과로 받아온 값(result
)를 콜백 함수 외부에서 사용할만한 방법이 필요함
let tempValue = null;
// 비동기 함수 대체
setTimeout(() => {
// result를 특정한 비동기처리 이후에 받아온 값이라고 가정
const result = 10;
tempValue = result;
}, 1000);
// 1) 여기서 사용
console.log(tempValue); // null
비동기 함수로부터 얻은 결괏값을 전역변수에 넣고 사용하려고 했지만 실제로 작성해 보면 null
값이 나옴
자바스크립트 작동 방식에 의해서 비동기 함수의 콜백 함수는 절 때 1)
에서 보다 먼저 작동할 수가 없음
( 1)
의 작동이 끝나고 난 후에 콜백 함수가 실행되는 순서가 뒤바뀔 수가 없음 )
// setTimeout의 콜백함수
const callback = f => {
const result = 20;
f(result)
};
// setTimeout의 콜백함수의 결과값을 사용하기위한 콜백함수
const callbackOfCallback = v => console.log(v);
setTimeout(callback, 1000, callbackOfCallback); // 20
// 분리안하고 사용
setTimeout(f => {
const result = 20;
f(result)
}, 1000, v => console.log(v));
비동기 함수에 콜백 함수(callbackOfCallback()
)를 인자로 넘겨줘서 비동기 함수의 처리가 완료될 시점에 콜백 함수(callbackOfCallback()
)를 호출해서 데이터를 사용하는 방법임
물론 정상적으로 처리는 되지만 가독성이 너무 안 좋음
또한 콜백함수로 처리할 경우에는 예외처리에 한계가 발생함
try{
setTimeout(() => throw new Error("강제 에러 발생"));
} catch (error) {
// 여기서 예외를 잡지 못함
console.error(error);
}
이러한 이유때문에 Promise
가 도입되었다.
const promise = new Promise((resolve, reject) => {
if(성공시) resolve("성공한 값");
else reject("실패한 에러");
});
[[PromiseState]]
모든 프로미스들은 [[PromiseState]]
로 내부 상태를 가진다.
1. pedding
: 반환안한 대기 상태 ( resolve()
, reject()
중 아무것도 안한 상태 )
2. fulfilled
: 성공을 선언한 상태 ( resolve()
)
3. rejected
: 실패를 선언한 상태 ( reject()
)
[[PromiseResult]]
resolve()
, reject()
의 인수로 전달한 값을 저장하는 내부 프로퍼티
[[PromiseState]]
가 fulfilled
이면 호출되며 첫 번째 인자로 [[PromiseResult]]
값을 받음
그리고 프로미스체이닝을 위해서 반환 값을 Promise로 변환후 반환한다.
[[PromiseState]]
가 rejected
이면 호출되며 첫 번째 인자로 [[PromiseResult]]
값을 받음
그리고 프로미스체이닝을 위해서 반환 값을 Promise로 변환후 반환한다.
무조건 한번 호출
const promiseResolve = new Promise((resolve, reject) => resolve("a"));
const promiseReject = new Promise((resolve, reject) => reject(new Error("에러 발생")));
// 1)의 v에는 최초 promiseResolve에서 resolve("a")한 값인 "a"가 들어감
// 이후에는 그 이전에 반환한 값이 v에 들어가는 형식임
// 그리고 1)에서는 명시적으로 프로미스를 생성했지만, 기본 값을 반환하는 경우 체이닝을 위해 암묵적으로 프로미스로 변환후 반환해줌
// 즉 1)의 코드와 2), 3)의 코드는 동일한 코드임
promiseResolve
.then(v => new Promise(r => r(v + "b"))) // 1) // "a" + "b"반환
.then(v => v + "c") // 2) // "ab" + "c"반환
.then(v => v + "d") // 3) // "abc" + "d"반환
.then(console.log); // 최종 결과값 : abcd
promiseReject
.then(v => v + 1)
.then(v => v + 1)
.then(v => v + 1)
.then(console.log)
.catch(error => console.error(error)); // reject호출이므로 앞의 then모두 무시하고 여기 실행
[[PromiseState]]
가 fulfilled
인 프로미스[[PromiseState]]
가 rejected
인 프로미스인수로 넘긴 값으로 성공한 프로미스 반환
인수로 넘긴 값으로 실패한 프로미스 반환
인수로 프로미스 배열을 넘기고 해당 배열안의 모든 프로미스가 성공할 때 까지 기다렸다가 전부 성공시 성공값을 배열로 반환
하지만 하나라도 실패시 즉시 종료하고 실패한 값 반환
인수로 프로미스 배열을 넘기고 가장 먼저 성공한 프로미스만 반환
중간에 실패할 경우 즉시 실패한 프로미스 반환
인수로 프로미스 배열을 넘기고 모든 프로미스가 완료될 때 까지 기다렸다가 값 배열로 반환
중간에 실패하더라도 멈추지 않고 모든 값을 받아서 반환함
Promise.resolve("a").then(console.log); // "a"
Promise.reject(new Error("Error 발생!")).catch(console.error); // error
Promise.all([
new Promise(r => setTimeout(() => r(1000), 1000)),
new Promise(r => setTimeout(() => r(2000), 2000)),
new Promise(r => setTimeout(() => r(3000), 3000)),
new Promise(r => setTimeout(() => r(4000), 4000)),
// Promise.reject(new Error("all Error"))
]).then(console.log); // 4초뒤에 [1000, 2000, 3000, 4000]
Promise.race([
new Promise(r => setTimeout(() => r(1000), 1000)),
new Promise(r => setTimeout(() => r(2000), 2000)),
new Promise(r => setTimeout(() => r(3000), 3000)),
new Promise(r => setTimeout(() => r(4000), 4000)),
// Promise.reject(new Error("all Error"))
]).then(console.log); // 1초뒤에 1000 / 4초뒤 종료
Promise.allSettled([
new Promise(r => setTimeout(() => r(1000), 1000)),
new Promise(r => setTimeout(() => r(2000), 2000)),
new Promise(r => setTimeout(() => r(3000), 3000)),
new Promise(r => setTimeout(() => r(4000), 4000)),
Promise.reject(new Error("all Error"))
]).then(console.log); // [{ status: 'fulfilled', value: 1000 }, ...] 형태로 반환
프로미스의 후속 처리 메서드의 콜백 함수들이 저장되는 큐이다.
일반 태스크 큐에 저장되는 콜백 함수들 보다 우선순위가 높아서 먼저 호출된다.
setTimeout(() => console.log(1), 0);
Promise.resolve()
.then(() => console.log(2))
.then(() => console.log(3));
// 2, 3, 1
// 프로미스가 우선순위가 높으므로 먼저 출력된다.