자바스크립트는 비동기 처리를 위한 하나의 패턴으로 콜백 함수를 사용
- 콜백 헬로 인해 가독성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란
- 여러 개의 비동기 처리를 한번에 처리하는 데 한계가 존재
→ ES6에서 비동기 처리를 위한 또 다른 패턴으로 프로미스를 도입
프로미스는 전통적인 콜백 패턴이 가진 단점을 보완하며, 비동기 처리 시점을 명확하게 표현할 수 있다는 장점이 존재한다.
프로미스의 생성
const promise = new Promise((resolve, reject) => { // Promise 함수의 콜백 함수 내부에서 비동기 처리를 수행한다. if (/* 비동기 처리 성공 */) { resolve('result'); } else { /* 비동기 처리 실패 */ reject('failure reason'); } });
- Promise 생성자 함수를 new 연산자와 함께 호출
- Promise는 호스트 객체가 아닌 ECMAScript 사양에 정의된 표준 빌트인 객체
// GET 요청을 위한 비동기 함수 const promiseGet = (url) => { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.send(); xhr.onload = () => { if (xhr.status === 200) { // 성공적으로 응답을 전달받으면 resolve 함수 호출 resolve(JSON.parse(xhr.response)); } else { // 에러 처리를 위해 reject 함수 호출 reject(new Error(xhr.status)); } }; }); }; // promiseGet 함수는 프로미스를 반환 promiseGet("https://jsonplaceholder.typicode.com/posts/1");
- 함수 내부에서 프로미스를 생성하고 반환
프로미스는 현재 비동기 처리가 어떻게 진행되고 있는지 나타내는 상태 정보를 가짐
프로미스의 상태 정보 | 의미 | 상태 변경 조건 |
pending | 비동기 처리가 아직 수행되지 않은 상태 | 프로미스가 생성된 직후 기본 형태 |
fulfilled | 비동기 처리가 수행된 상태(성공) | resolve 함수 호출 |
rejected | 비동기 처리가 수행된 상태(실패) | reject 함수 호출 |
생성된 직후의 프로미스는 기본적으로 pending 상태, 이후 비동기 처리가 수행되면 비동기 처리 결과에 따라 프로미스의 상태가 변경
프로미스의 후속 처리 메서드
프로미스는 후속 메서드 then, catch, finally를 제공
프로미스의 비동기 처리 상태가 변화하면 후속 처리 메서드에 인수로 전달한 콜백 함수가 선택적으로 호출
모든 후속 처리 메서드는 프로미스를 반환, 비동기로 동작
then
// fulfilled new Promise((resolve) => resolve("fulfilled")).then( (v) => console.log(v), (e) => console.error(e) ); // fulfilled // rejected new Promise((_, reject) => reject(new Error(rejected))).then( (v) => console.log(v), (e) => console.error(e) ); // Error: rejected
catch
- 한 개의 콜백 함수를 인수로 전달
- catch 메서드의 콜백 함수는 프로미스가 rejected인 경우에만 호출
finally
- 한 개의 콜백 함수를 인수로 전달
- finally 메서드의 콜백 함수는 프로미스의 성공 또는 실패와 상관없이 무조건 한 번 호출
- 프로미스의 상태와 상관없이 공통적으로 수행해야 할 처리 내용이 있을 때 유용
프로미스 체이닝
- 콜백 헬은 해결하지만 프로미스 후속 처리 메서드를 연속적으로 호출하는 프로미스 체이닝 발생
- 프로미스도 콜백 패턴을 사용하므로 콜백 함수를 사용하지 않는 것은 아님.
콜백 패턴은 가독성이 좋지 않으며, 이 문제는 ES8에서 도입된 async/await를 통해 해결 가능
→ 프로미스의 후속 처리 메서드 없이 마치 동기 처리처럼 프로미스가 처리 결과를 반환하도록 구현 가능
async/await도 프로미스를 기반으로 동작 → 프로미스를 잘 이해하기!
마이크로태스크 큐
어떤 순서로 로그가 출력될까?
setTimeout(() => console.log(1), 0); Promise.resolve() .then(() => console.log(2)) .then(() => console.log(3));
정답
2 → 3 → 1
- 프로미스의 후속 처리 메서드의 콜백 함수는 태스크 큐가 아닌 마이크로태스크 큐에 저장
- 마이크로태스크 큐는 태스크 큐와는 별도의 큐이며, 프로미스의 후속 처리 메서드의 콜백 함수가 일시 저장된다.
- 비동기 함수의 콜백 함수나 이벤트 핸들러는 태스크 큐에 임시 저장
- 콜백 함수나 이벤트 핸들러를 일시 저장한다는 점에서 태스크 큐와 동일하지만, 마이크로태스크 큐는 태스크 큐보다 우선순위가 높다.
fetch
- XMLHttpRequest 객체와 마찬가지로 HTTP 요청 전송 기능을 제공하는 클라이언트 사이드 Web API
- XMLHttpRequest 객체보다 사용법이 간단하고 프로미스를 지원하기 때문에 비동기 처리를 위한 콜백 패턴의 단점에서 자유로움.
- 비교적 최근에 추가된 Web API로서 대부분의 모던 브라우저에서 제공
- fetch 함수에는 HTTP 요청을 전송할 URL과 HTTP 요청 메서드, HTTP 요청 헤더, 페이로드 등을 설정한 객체를 전달
const promise = fetch(url, [, options])
- fetch 함수는 HTTP 응답을 나타내는 Response 객체를 래핑한 Promise 객체를 반환
fetch("url").then((response) => console.log(response));
- then을 통해 프로미스가 resolve한 Response 객체를 전달받을 수 있음.