티스토리 뷰
11.6 Promisification
콜백을 받는 함수를 프라미스 반환 함수로 바꾸는 것을 프라미스화라 한다.
function loadScript(src, callback) {
let script = document.createElement('script');
script.src = src;
script.onload = () => callback(null, script);
script.onerror = () => callback(new Error(`${src}를 불러오는 도중에 에러가 발생함`));
document.head.append(script);
}
// 사용법:
// loadScript('path/script.js', (err, script) => {...})
// ------------------- 프라미스화
let loadScriptPromise = function(src) {
return new Promise((resolve, reject) => {
loadScript(src, (err, script) => {
if (err) reject(err)
else resolve(script);
});
})
}
// 사용법:
// loadScriptPromise('path/script.js').then(...)
여러개의 함수를 프라미스화 해야하기 때문에 이를 대비한 헬퍼함수를 만들어보자. (코드를 하나하나 뜯어보면서 다시 볼 필요가 있겠다.)
function promisify(f) {
return function (...args) { // 래퍼 함수를 반환함
return new Promise((resolve, reject) => {
function callback(err, result) { // f에 사용할 커스텀 콜백
if (err) {
reject(err);
} else {
resolve(result);
}
}
args.push(callback); // 위에서 만든 커스텀 콜백을 함수 f의 인수 끝에 추가합니다.
f.call(this, ...args); // 기존 함수를 호출합니다.
});
};
};
// 사용법:
let loadScriptPromise = promisify(loadScript);
loadScriptPromise(...).then(...);
근데 위 함수는 콜백 함수가 두개의 파라미터만 받는 것을 가정하고 만들어졌다. 그보다 훨씬 많은 파라미터를 가진 콜백함수의 경우 어떻게 해야될 까?
// 콜백의 성공 결과를 담은 배열을 얻게 해주는 promisify(f, true)
function promisify(f, manyArgs = false) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, ...results) { // f에 사용할 커스텀 콜백
if (err) {
reject(err);
} else {
// manyArgs가 구체적으로 명시되었다면, 콜백의 성공 케이스와 함께 이행 상태가 됩니다.
resolve(manyArgs ? results : results[0]);
}
}
args.push(callback);
f.call(this, ...args);
});
};
};
// 사용법:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...)
코드가 익숙하지 않아서 그런지 굉장히 혼란스럽다... 차근차근 살펴봐야할 것 같다.
11.7 Microtasks
아래 예시를 수행해보면 이상한 점이 있다.
let promise = Promise.resolve();
promise.then(() => alert("프라미스 성공!"));
alert("코드 종료"); // 얼럿 창이 가장 먼저 뜹니다.
alert 대신 console.log로 바꿔서 수행했다.
프라미스가 즉시 이행 상태가 되었는데도 코드 종료 메시지가 먼저 나온 뒤 수행되었다. 이유가 뭘까?
Microtasks queue
- 마이크로태스크 큐는 먼저 들어온 작업을 먼저 실행(FIFO, first-in-first-out).
- 실행할 것이 아무것도 남아있지 않을 때만 마이크로태스크 큐에 있는 작업이 실행되기 시작함.
이 큐의 동작 방식에 의해 프라미스가 준비되면 핸들러가 큐에 들어간다. 이 때 핸들러는 아직 실행되지 않은 상태고, 현재 코드로부터 자유로워졌을 때 큐에서 작업을 실행한다. 그러면 원래 의도한대로 프라미스 성공을 먼저 출력하고 싶다면 다음과 같이 작성하면 된다.
Promise.resolve()
.then(() => alert("프라미스 성공!"))
.then(() => alert("코드 종료"));
Unhandled rejection
처리되지 못한 거부(unhandled rejection)은 이러한 마이크로태스크 큐 끝에서 프라미스 에러가 처리되지 못했을 때 발생한다. 보통은 .catch를 통해 처리하겠지만(개발자가), 만약 그러지 못했다면 엔진이 unhandledrejection 이벤트를 트리거한다.
let promise = Promise.reject(new Error("프라미스 실패!"));
// 프라미스 실패!
window.addEventListener('unhandledrejection', event => alert(event.reason));
만약, 에러를 의도적으로 나중에 처리하도록 한다면 어떻게 될까? 아래 예시를 보자.
let promise = Promise.reject(new Error("프라미스 실패!"));
setTimeout(() => promise.catch(err => alert('잡았다!')), 1000);
// Error: 프라미스 실패!
window.addEventListener('unhandledrejection', event => alert(event.reason));
위 예시에서 catch는 1초 후에 수행되도록 했다. 에러를 잡긴 하지만, 마이크로태스크 큐에 있는 작업이 모두 완료된 후 처리될 것이기 때문에 unhandledrejection 이벤트가 먼저 발생하게 된다. 이후 catch에 의해 에러가 잡힌다.
'스터디 > JavaScript' 카테고리의 다른 글
모던 자바스크립트 튜토리얼 part1 - 13.1 ~ 3 (1) | 2024.05.28 |
---|---|
모던 자바스크립트 튜토리얼 part1 - 11.8 (0) | 2024.05.28 |
모던 자바스크립트 튜토리얼 part1 - 11.5 (0) | 2024.05.27 |
모던 자바스크립트 튜토리얼 part1 - 11.4 (0) | 2024.05.27 |
모던 자바스크립트 튜토리얼 part1 - 11.3 (0) | 2024.05.27 |