출처 : https://preamtree.tistory.com/168
비동기 상황에서 예외와 스택 트레이스
비동기 함수에서 예외 발생 시 스택 트레이스가 출력되지 않는다.
스택 트레이스가 없어지는 상황
async function funcOne() {
throw new Error('Error here prints the complete stack');
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
});
}
async function funcTwo() {
await funcOne();
}
async function funcThree() {
await funcTwo();
}
funcThree().catch(err => console.error(err));
Promise 의 setTimeout 비동기 함수가 평가되기 전에 예외를 던졌기 때문에
콜스택에 funcOne, funcTwo, funcThree 함수가 남아있다.
async function funcOne() {
await new Promise(resolve => {
setTimeout(() => {
resolve();
}, 1000);
throw new Error('Error here prints the complete stack');
});
}
async function funcTwo() {
await funcOne();
}
async function funcThree() {
await funcTwo();
}
funcThree().catch(err => console.error(err));
Promise 의 setTimeout 비동기 함수가 평가되었기 때문에
콜스택에 funcOne, funcTwo, funcThree 함수가 남아있지 않다.
사라진 스택 트레이스를 출력하는 방법
-
node.js 12 버전부터 –async-stack-traces 옵션을 통해 비동기 상황에서
예외 발생 시 스택 트레이스를 기억해 출력하도록 할 수 있다.
최신 브라우저 콘솔에는 기본으로 적용되어 비동기 상황에서 스택 트레이스가 출력된다. -
Node.js 의 fs 네이티브 모듈의 비동기 함수의 에러는 스택 트레이스를 출력하지 않는다.
모듈 개발자가 방법을 찾아보려 했지만 성능 문제를 극복하지 못해 아직 해결하지 못했다고 한다. (https://github.com/nodejs/node/issues/30944)
비동기 함수의 가장 가까운 곳에서 에러를 catch 해 스택 트레이스를 생성할 수 있도록 개발해 문제를 해결했다. 비동기 fs 함수를 Promise 로 감싸고 에러 발생 시 reject 하도록 한다.
발생된 reject 를 promise-catch 해 CustomAsyncError 객체를 만들어 다시 throw 한다.
CustomAsyncError 는 throw 된 지점에서 스택 트레이스를 생성하고, 생성된 스택 트레이스에 reject 에서 전달한 err.stack 을 추가하도록 수정했다.
// async fs function
await new Promse((resolve, reject) => {
const stream = fs.createReadStream(this.filepath);
stream
.on('error', err => reject(err))
.on('open', () => resulve(stream));
}).catch(err => {
throw new CustomAsyncError(err);
});
// custom async error object
class CustomAsyncError extends Error {
constructor(err) {
...
super(err.message);
if (Error.captureStacktrace) {
Error.captureStackrace(this, CustomAsyncError);
}
if (err) {
const idx = this.stack.indexOf('\n');
this.stack = err.stack + '\n' + this.stack.substring(idx + 1);
}
}
}