❓ 전통적인 js 비동기 프로그래밍의 역사
1) Callback
2) Promise
3) Generator
4) Async / Await
Promise 란?
비동기 작업의 성공/실패 결과를 다루기 위한 객체이다.
promise는 callback pattern의 단점을 극복하기 위해 출현하였다.
callback pattern 은 콜백 지옥, 가독성 저하, 에러 처리 어려움 등의 문제가 있었다.
callback pattern 과 다르게 promise는 안전하며, 유지보수 쉬운 코드작성이 가능하다.
promise는 다음 3가지 상태를 가진다.
- pending: 아직 완료되지 않음
- fulfilled: 성공적으로 완료됨 (resolve)
- rejected: 실패함 (reject)
pending → fulfilled or rejected → settled (fulfilled 또는 rejected 상태 둘다 포함하는 표현으로 작업이 완료됨을 의미한다.)
Promise . then( ) . catch( ) . finally( ) ... chaining까지
실제 Promise의 구조 요약버전
class Promise {
constructor(nbfn) {
this.state = 'pending';
this.value = null;
this.thenFns = [];
this.catchFn = null;
this.finallyFn = null;
const resolve = this.runSuccess.bind(this);
const reject = this.runFail.bind(this);
try {
nbfn(resolve, reject);
} catch (e) {
reject(e);
}
}
runSuccess(ret) {
if (this.state !== 'pending') return;
this.state = 'fulfilled';
this.value = ret;
let t = ret;
for (const fn of this.thenFns) {
try {
t = fn(t);
} catch (e) {
this.runFail(e);
return;
}
}
if (this.finallyFn) this.finallyFn(); // ✅ 성공 시 finally 실행
}
runFail(err) {
if (this.state !== 'pending') return;
this.state = 'rejected';
this.value = err;
if (this.catchFn) {
this.catchFn(err);
} else {
console.error('Uncaught Promise rejection:', err);
}
if (this.finallyFn) this.finallyFn(); // ✅ 실패 시 finally 실행
}
then(fn) {
this.thenFns.push(fn);
return this;
}
catch(errFn) {
this.catchFn = errFn;
return this;
}
finally(finalFn) {
this.finallyFn = finalFn;
return this;
}
}
예시
new Promise((resolve, reject) => {
console.log('1️⃣ executor 시작');
setTimeout(() => {
const success = Math.random() > 0.5;
if (success) {
resolve('✅ 성공 데이터');
} else {
reject('❌ 실패 사유');
}
}, 1000);
})
.then((val) => {
console.log('2️⃣ then 처리:', val);
return val + ' [처리됨]';
})
.then((val) => {
console.log('3️⃣ 또다른 then:', val);
})
.catch((err) => {
console.log('4️⃣ catch 처리:', err);
})
.finally(() => {
console.log('5️⃣ finally는 무조건 실행됨!');
});
성공한 경우 (Math.random() > 0.5)
1️⃣ executor 시작
2️⃣ then 처리: ✅ 성공 데이터
3️⃣ 또다른 then: ✅ 성공 데이터 [처리됨]
5️⃣ finally는 무조건 실행됨!
실패한 경우 (Math.random() <= 0.5)
1️⃣ executor 시작
4️⃣ catch 처리: ❌ 실패 사유
5️⃣ finally는 무조건 실행됨!
- 각 then()은 새로운 Promise를 반환하고, 그 값은 다음 then() 에 전달된다. return 값은 다음 then의 입력값이 된다.
- then에서의 반환 값이 일반값일 경우 바로 다음 then에 값을 전달하고 promise 객체인 경우 resolve 될때까지 기다렸다가 실행된다.
예를들어, 글을 받아온 다음에 실행 그 글에 댓글을 받아온 다음에 실행 -> p(id).then((id)=>글).then((글)=>댓글)
그래서 promise를 연속으로 사용하고 싶을때 then 체이닝을 많이 쓰고 사실 await을 쓰면 이 짓을 안해도됨 - throw 나 오류가 발생하면 then 체이닝을 중단하고 에러가 catch의 입력값이 되어 .catch()로 이동한다.
- finally는 체이닝의 중간이나 마지막에 넣을 수 있으며 성공, 실패와 무관하게 항상 실행된다.
Promise class static method
Promise.resolve(x)
이미 존재하는 값 x를 곧바로 fulfilled(성공) 상태인 Promise로 감싸준다.
Promise.resolve(42).then(val => console.log(val));
// 👉 출력: 42
new Promise((resolve) => resolve(42)).then(console.log);
// 두 코드는 완전히 같은 코드이다.
Promise.reject(e)
Promise.reject(e)는 즉시 rejected 상태인 Promise를 만든다.
Promise.reject(new Error('에러!')).catch(console.error);
new Promise((_, reject) => reject(new Error('에러!'))).catch(console.error);
//두 코드는 완전히 같은 코드이다.
예시
x: Promise = b ? promi : Promise.resolve(y);
Promise 객체인 x에 값을 넣어줄 때 이미 promise인 promi 넣거나 y 를 즉시 성공상태인 Promise 객체로 만들어서 타입을 통일해 줄 수 있다.
Promise.all
- 여러 프로미스의 Fn을 동시에 실행한다.
- 모두 성공(fulfilled) 시 시간과 무관하게 순서를 보장한다. (결과순서가 아닌 입력순서를 보장한다는 뜻)
- 하나라도 실패 시 바로 catch로 이동한다.
Promise.all([p1, p2, p3])
.then(results => console.log(results)) // [res1, res2, res3]
.catch(error => console.error(error));
Promise.race
- 가장 먼저 완료된 하나만을 반환한다.
- 나머지는 무시된다.
- 가장 빨리 끝난 하나가 실패하면 catch로 이동한다.
Promise.race([p1, p2])
.then(res => console.log(res)) // 가장 먼저 끝난 Promise의 결과
.catch(err => console.error(err)); // 가장 먼저 실패한 Promise가 있다면 바로 catch
Promise.any
- 여러 프로미스 중에서 가장 먼저 성공한 하나만을 반환한다.
- 단 하나라도 성공한다면 .then을 실행한다.
- 모두 실패해야 catch 로 이동한다.
Promise.any([p1, p2, p3])
.then(res => console.log(res)) // 가장 빨리 성공한 하나
.catch(err => console.error(err)); // 모두 실패한 경우에만 catch
Promise.allSettled
- 여러 프로미스 Fn 이 성공이건 실패건 모두 settled 될때까지 기다린다.
- 입력순서가 보장된다.
- 결과는 각각의 status(fulfilled 또는 rejected), value, reason을 포함한 객체 배열로 반환된다.
- catch를 쓰지 않고도 실패 정보를 알 수 있다.
Promise.allSettled([p1, p2, p3])
.then(results => console.table(results));
// 결과 예시
[
{ status: 'fulfilled', value: 1 },
{ status: 'rejected', reason: 'Error!' },
{ status: 'fulfilled', value: 2 },
]
Micro Task Queue
프로미스는 Task Queue 가 아니라 무조건 Micro Task Queue에 들어간다.
따라서 Micro Task Queue에 프로미스가 쌓여있다면, 이게 무조건 우선 시 되기 때문에 Task Queue에 있는 것들은 대기하게 된다.


❓fetch
fetch(url[, options])는 브라우저나 Node.js(v18 이상)에서 제공하는 비동기 HTTP 요청 함수이며, 실행 시
Promise<Response> 객체를 반환한다.
fetch('https://jsonplaceholder.typicode.com/users/1')
.then(response => {
return response.json(); // 다시 Promise 반환
})
.then(data => {
console.log(data); // 실제 JSON 데이터 출력
})
.catch(error => {
console.error('에러 발생:', error);
});
// fetch()는 Response 객체를 담은 Promise를 반환하고,
// json()은 그 Response를 파싱해 다시 Promise를 반환한다.'Dev > Javascript' 카테고리의 다른 글
| [JS] EventLoop (0) | 2025.05.22 |
|---|---|
| [JS] 비동기 프로그래밍 (1) | 2025.05.20 |
| [JS] 배열 (Array) (1) | 2025.04.15 |
| [JS] this관련 예제 파헤치기 (0) | 2025.04.11 |
| [JS] 객체와 프로퍼티 (0) | 2025.04.10 |
