자바스크립트 엔진은 비동기 처리를 제공하지 않는다. 하지만 대부분의 작업들이 비동기로 이루어지는 Javascirpt는 어떤 식으로 처리하는 것일까?
자바스크립트에서 비동기 코드는 API를 통해 활용할 수 있다. 예를 들자면, setTimeout, XMLHttpRequest, fetch 등의 Web API와 파일 처리 API, 암호화 API를 node.js가 제공한다.
비동기 처리모델
비동기 코드를 처리하는 모듈은 자바스크립트 엔진 외부에 있다. event loop, task queue, job queue 등으로 구성된다. API 모듈은 비동기 요청을 처리 한후 태스크 큐에 콜백 함수를 넣는다. JS 엔진은 콜 스택이 비워지면 태스크 큐의 콜백 함수를 실행한다.
Promise
Promise 는 비동기 작업을 표현하는 JS 객체이다. 진행, 성공, 실패 상태를 표현하며, 처리의 순서를 표현할 수 있다.
JS는 비동기 처리를 위한 하나의 패턴으로 콜백 함수를 사용한다. 하지만 전통적인 콜백 패턴은 콜백 헬로 인해 가동성이 나쁘고 비동기 처리 중 발생한 에러의 처리가 곤란하며 여러 개의 비동기 처리를 한번에 처리하는 데도 한계가 있다.
ES6에서는 비동기 처리를 위한 또 다른 패턴으로 프로미스를 도입했다. 프로미스는 전통적인 콜백 패턴이 가진 단점을 보완하며 비동기 처리시점을 명확하게 표현할 수 있다는 장점이 있다.
//프로미스 생성
const promise = const promise = new Promise((resolve, reject)=>{
if(/* 비동기 처리 성공*/){
resolve('result');
} else {
// 비동기 처리 실패
reject('failure reason');
}
});
프로미스는 비동기 처리가 어떻게 진행되고 있는지를 나타내는 상태 정보를 갖는다.
- pending : 비동기 처리가 아직 수행되지 않은 상태 // 프로미스가 생성된 직후 기본 상태
- fulfilled : 수행된 상태 (성공) // resolve 함수 호출
- rejected : 수행된 상태 (실패) // reject 함수 호출
비동기 처리가 수행된 상태를 settled라고 한다. 즉, fulfilled, rejected가 settled의 상태라고 말할 수 있다.
프로미스의 정적 메서드
Promise는 5가지 정적 메서드를 제공한다.
1. Primise.resolve / Promise.reject
resolve 와 reject 메서드는 프로미스를 생성하기 위해 이미 존재하는 값을 래핑한다.
// 배열을 resolve하는 프로미스를 생성
const resolvedPromise = Promise.resolve([1,2,3]);
resolvedPromise.then(console.log); // [1,2,3]
//위와 같은 동작
const resolveNewPromise = new Promise(resolve => resolve([1,2,3]));
resolveNewPromise.then(console.log); // [1,2,3]
2. Promise.all
여러 개의 비동기 처리를 모두 병렬 처리할 때 사용한다. 모든 프로미스가 fulfilled 상태가 되면 종료한다. 만약 하나라도 rejected 상태가 되면 나머지 프로미스가 fulfilled 상태가 되는 것을 기다리지 않고 즉시 종료한다.
3. Promise.race
여러개의 프로미스 중 가장 먼저 fulfilled 상태가 된 프로미스의 처리 결과를 반환한다. 만약 rejected 상태가 되면 전달된 프로미스가 하나라도 rejected 상태가 되면 에러를 reject하는 새로운 프로미스를 반환한다.
4. Promise.allSettled
전달받은 프로미스가 모두 settled 상태가 되면 처리 결과를 배열로 반환한다. ES11에 도입된 메서드이고 IE를 제외한 대부분의 모던 브라우저에서 지원한다.
fetch
HTTP 요청 전송 기능을 제공하는 클라이언트 사이드 Web API이다. XMLHttpRequest 객체보다 사용법이 간단하고 프로미스를 지원하기 때문에 비동기 처리를 위한 콜백 패턴의 단점에서 자유롭다. IE를 제외한 대부분의 모던 브라우저에서 제공한다.
fetch 함수에는 HTTP 요청을 전송할 URL, HTTP 요청 메서드, HTTP 요청 헤더, 페이로드 등을 설정한 객체를 전달한다.
const promise = fetch(url [, options]);
HTTP 응답을 나타내는 Response 객체를 래핑한 Promise 객체를 반환한다. 첫 번째 인수로 HTTP 요청을 전송할 URL만 전달하면 GET 요청을 전송한다.
fetch 함수는 HTTP 응답을 나타내는 Response 객체를 래핑한 프로미스를 반환하므로 후속 처리 메서드 tehn을 통해 프로미스가 resolve한 Response 객체를 전달받을 수 있다. Response 객체는 HTTP 응답을 나타내는 다양한 프로퍼티를 제공한다.
Response.prototype에는 Response 객체에 포함되어 있는 HTTP 응답 몸체를 위한 다양한 메서드를 제공한다.
fetch('https://jsonplaceholder.typicode.com/todos/1')
// response는 HTTP 응답을 나타내는 Response 객체이다.
.then(response => response.json())
// json은 역직렬화된 HTTP 응답 몸체이다.
.then(json => console.log(json));
fetch 함수를 사용할 때는 에러 처리에 주의해야 한다.
const wrongUrl = 'https://jsonplaceholder.typicode.com/xxx/1';
fetch(wrongUrl)
// response는 HTTP 응답을 나타내는 Response 객체이다.
.then(() => console.log('ok'))
// json은 역직렬화된 HTTP 응답 몸체이다.
.then(() => console.log('error'));
부적절한 URL을 지정했기 떄문에 404 에러가 발생한다. catch 후속 처리 메서드에 의해 error가 출력될 것처럼 보이지만 ok 가 출력된다.
fetch 함수가 반환하는 프로미스는 기본적으로 404, 500과 같은 HTTP 에러가 발생해도 에러를 reject하지 않고 불리언 타입의 ok 상태를 false로 설정한 Response 객체를 resolve한다. 오프라인 등의 네트워크 장애나 CORS 에러에 의해 요청이 완료되지 못한 경우에만 프로미스를 reject한다.
따라서 fetch 함수를 사용할 때는 다음과 같이 fetch 함수가 반환한 프로미스가 resolve한 불리언 타입의 ok상태를 확인해 명시적으로 에러를 처리해야 한다.
const wrongUrl = 'https://jsonplaceholder.typicode.com/xxx/1';
fetch(wrongUrl)
// response는 HTTP 응답을 나타내는 Response 객체이다.
.then(response => {
if(!response.ok) throw new Error(response.statusText);
return response.json();
})
.then(todo => console.log(todo))
.catch(err => console.error(err));
'개발지식 > JavaScript' 카테고리의 다른 글
이게(this) 뭐야 (0) | 2022.08.26 |
---|---|
bind() 가 뭐야 (0) | 2022.04.18 |
배열 (0) | 2022.02.10 |
함수형 메서드들의 오야지 reduce (0) | 2022.02.10 |
진수 변환 (0) | 2022.02.10 |