티스토리 뷰
자바스크립트는 전통적으로 비동기 처리를 위해 콜백함수를 사용한다. 하지만 콜백 패턴은 콜백헬(타고타고 들어가야됨)로 가독성이 나쁘고 비동기를 한꺼번에 처리하기도 어려운 데다가, 비동기 에러처리가 어렵다(각 콜백안에서 처리해야함).
45.1 비동기 처리를 위한 콜백 패턴의 단점.
- 예제에서 함수 내에 비동기 함수가 있는 경우를 보여주었다.
- caller함수 내에 비동기 함수가 있는 경우 비동기의 결과를 그 해당 caller가 리턴하거나 변수에 할당하면 동작하지 않는다. 비동기 처리결과에 대한 후속처리나 에러처리는 해당 비동기 함수 내에서 처리를 해야한다. 이게 중첩되면 엄청나게 복잡해질 수 있다.
- caller함수가 호출되면 해당 함수의 컨텍스트가 생성되고 콜스택에 함수들이 푸시된다. 그런데, 그 안에 비동기함수가 있으면 콜스택에 푸시되지않고 태스크 큐에 저장되어 대기하다가, 콜스택이 비었을 때 푸시된다. 이벤트 핸들러는 이벤트 핸들러에게 평가를 받은 후에야 컨텍스트를 생성하고 콜스택에 푸시된다(역시 콜스택이 비었을 때 푸시된다.).
45.1.2 콜백 패턴 에러처리의 한계
- 콜백 함수가 호출될 당시에는 콜러함수가 이미 콜스택에서 비워진 후이므로 에러를 전파할 곳이 없다. 그래서 해당 함수 내에서 에러처리를 해야한다.
45.2 프로미스의 생성 new Promise
프로미스는 비동기 처리상태와 결과를 관리하는 객체다.
- promise는 호스트객체가 아닌 빌트인 객체다?
- 성공하면 fufilled -> resolve함수 호출. 처리결과값(PromiseValue)으로는 1을 가진다.
- 실패하면 reject -> reject함수 호출. 처리결과값(PromiseValue)으로는 Error객체를 가진다.
- 처리중이면 pending
- 프로미스로 반환하게 되면 처리가 되지 않은 상태에서 caller에게 return을 해줄 수 있다.
45.3 프로미스의 후속처리 메서드
** then, catch, finally **
- then 메서드는 두개의 콜백함수 인자를 받는다. 콜백 성공시 처리결과를 인수로 받는 콜백, 콜백 실패시 처리결과를 인수로 받는 콜백. 그리고 promise를 반환한다.
45.3.2 promise.prototype.catch
- catch 메서드는 rejected상태인 경우에만 호출되는 한개의 콜백함수를 인자로 받는다.
- then 두번째 메서드와 똑같이 에러 객체를 인자로 받는다. 콜백 성공시 후속처리가 필요하지는 않은데 에러처리는 필요한 경우 사용한다.
45.3.2 promise.prototype.finally
- finally 메서드는 한개의 콜백함수를 인자로 받는다. 그리고 성공 실패여부와 상관없이 무조건 호출된다. 프로미스 상태과 상관없이 후속처리를 해줄 경우 사용한다.
- .then(()=>{}).catch(()=>{}).finally(()=>{}) 이런 식으로 사용하면 완벽하다.
45.4 프로미스의 에러 처리
- then의 두번째 콜백함수 또는 catch를 사용해서 처리한다.
- catch는 then(undefined, onRejected)를 내부적으로 호출한다.
- then의 두번째 인자로 주는 것보다 따로 catch구문을 사용해주는게 가독성에 좋다.
45.5 프로미스 체이닝
- then catch finally는 프로미스를 반환하므로 연속적으로 처리 가능하다.
- 후속처리 메서드가 promise 객체가 아닌 것으로 반환하더라도 resolve또는 reject하여 프로미스로 감싸서 리턴한다.
- 다만 프로미스도 결국 콜백패턴을 사용하므로 콜백함수를 사용하는 것은 아니다?
- 콜백패턴은 가독성이 좋지 않다 그래서 async await를 사용할 수 있다. es8(2017)에서야 나왔다.
45.6 프로미스의 정적 메서드
- Promise는 주로 생성자 함수로 생성되지만 결국 객체이므로 메서드를 가질 수도 있다.
- Promise는 5가지 정적 메서드를 제공한다. resolve, reject, all, race, allSettled
45.5.1 Promise.resolve / Promise.reject
- resolve 메서드는 인수로 받은 값을 resolve하는 프로미스를 생성한다.
- reject 메서드는 인수로 받은 Error객체를 reject하는 프로미스를 생성한다.
45.6.2 Promise.all
- all 메서드는 여러개의 비동기처리를 병렬 처리할때 사용한다.
- Promise.all([ 1번 promise, 2번 프로미스, .. ]) 이런 식을 사용한다. 여기에 .then을 붙여서 프로미스들에 대한 에러를 일괄처리할 수도 있다.이중에 한개라도 에러가나면 전체에러가 나는 것이다. 중간에 에러가나면 나머지는 중간에 멈춰버린다. 그리고 에러가난 프로미스의 에러를 전달받는다.
- resolve처리가 되면 성공 결과를 배열로 리턴받는다.
45.6.3 Promise.race
- race는 all과 비슷한데, 다만 처리결과를 가장 먼저 fulfiled 되는 프로미스함수의 결과를 리턴한다. 빠르게 처리된애를 쓰고 싶을때 사용한다.
45.6.4 Promise.allSetteled
- allSetteled도 all과 비슷한데, 프로미스가 모두 완료된 상태(settled)에서야 반환한다. es11(2020)에서야 등장했다. allSettled를 반환받은 시점에는 setteld 이후 상태가 담겨있다는 것에 주의한다.
45.7 마이크로태스크큐
- 프로미스의 후속처리 메서드는 태스크 큐가 아닌 마이크로 태스크 큐에 저장된다.
- 마이크로 태스크큐는 태스크 큐와는 별개다. 그리고 마이크로 태스크 큐는 태스크 큐보다 우선순위가 높다. 이벤트 루프는 마이크로 태스크 큐에 담긴애들을 먼저 푸시하고 마이크로 큐가 빈 다음에서야 태스크큐를 푸시하고 실행한다.
45.8 fetch
- fetch함수는 XMLHttpRequest 객체롸 마찬가지로 클라이언트사이드에서 서버에 request를 날리기 위한 함수이다. 그런데, XMLHttpRequest보다 사용법이 간단하고 Promise를 지원하기 때문에, 콜백패턴의 단점에서도 자유롭다. Respose객채를 래핑한 promise객체를 리턴한다. 응답 body를 취득하려면 Response.prototype.json을 사용한다.
- Response.prototype.json은 resposebody를 취득하여 역직렬화한다. .then(response => {response.json}).then(json => {}) 이런식으로 사용한다.
- get post put delete patch를 사용해보자.
궁금한점
- 이벤트핸들러함수가 콜백함수가 푸시되는중에 푸시가되면 어떻게 되는지??
- 호스트 객체 빌트인 객체? ECMA에 정의되어 있는 객체는 빌트인객체, ECMA 정의에는 없는데 환경(브라우저, 노드)에서 제공해주는 객체를 호스트객체라고 한다.
- 다만 프로미스도 결국 콜백패턴을 사용하므로 콜백함수를 사용하는 것은 아니다? 가 무슨 말인지..?