[英]How can I see the full stack trace of error in setTimeout with a promise
我試圖了解如何從由setTimeout
引起的承諾拒絕中獲得完整的堆棧跟蹤
我正在運行以下示例:
'use strict';
function main() {
f1().catch(e => {
console.error('got error with trace:');
console.error(e);
});
f2().catch(e => {
console.error('got error with trace:');
console.error(e);
});
}
async function f1() {
return new Promise((resolve, reject) => {
reject(new Error('Error in normal flow'));
});
}
async function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error('Error in timeout'));
}, 0);
});
}
main();
我得到這個輸出:
got error with trace:
Error: Error in normal flow
at Promise (/Users/me/project/example.js:25:12)
at Promise (<anonymous>)
at f2 (/Users/me/project/example.js:24:10)
at main (/Users/me/project/example.js:9:3)
at Object.<anonymous> (/Users/me/project/example.js:29:1)
at Module._compile (module.js:569:30)
at Object.Module._extensions..js (module.js:580:10)
at Module.load (module.js:503:32)
at tryModuleLoad (module.js:466:12)
at Function.Module._load (module.js:458:3)
got error with trace:
Error: Error in timeout
at Timeout.setTimeout [as _onTimeout] (/Users/me/project/example.js:18:14)
at ontimeout (timers.js:488:11)
at tryOnTimeout (timers.js:323:5)
at Timer.listOnTimeout (timers.js:283:5)
如何使使用 setTimeout 啟動的承諾的堆棧跟蹤更冗長,就像沒有setTimeout
的承諾一樣?
當我在實際生產代碼中遇到這種情況時,我無法確切知道錯誤是從哪里開始的。 這使得調試非常困難。
我以前做過類似以下的事情:
async function f2() {
return new Promise((resolve, reject) => {
const potentialError = new Error('Error in timeout');
setTimeout(() => {
reject(potentialError);
}, 0);
});
}
這將導致錯誤,就像setTimeout
之外的錯誤一樣。 無論您在哪里創建錯誤,都將決定您的堆棧跟蹤。
對我來說,一個用例是測試超時,因為承諾永遠不會解決(在我的情況下,使用 puppeteer 和 mocha/jasmine)。 因為超時沒有有意義的堆棧跟蹤,我圍繞包含setTimeout
的承諾編寫了一個包裝器,很像這個例子。
我會嘗試這樣寫:
async function f() {
await new Promise((resolve, reject) => {
setTimeout(resolve, 0);
});
throw new Error('Error after timeout');
}
盡量避免在非承諾回調中做任何事情。
這里有一些不錯的答案,但尚未考慮的是生成堆棧跟蹤的成本很高,而且您不想一直這樣做。 V8 有一個花哨的 JIT 編譯器,它可以亂序運行,並且必須展開才能獲得正確的堆棧跟蹤。 因此,我們應該只在需要時為“潛在錯誤”生成堆棧跟蹤。
async function f2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Stack trace is unhelpful here.
reject(new Error("Error in timeout"))
}, 0)
})
}
async function f3() {
// This stack trace is useful here, but generating a stack trace is expensive
// because V8 has to unravel all of its fancy JIT stuff.
const potentialError = new Error("Error in timeout")
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(potentialError)
}, 0)
})
}
async function f4() {
try {
const result = await new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("Error in timeout"))
}, 0)
})
return result
} catch (error) {
// Override the stack trace only when you need it.
error.stack = new Error().stack
throw error
}
}
我已經這樣解決了: https : //github.com/NaturalCycles/js-lib/blob/master/src/promise/pTimeout.ts
pTimeout
函數代碼:
export async function pTimeout<T>(promise: Promise<T>, opt: PTimeoutOptions): Promise<T> {
const { timeout, name, onTimeout, keepStackTrace = true } = opt
const fakeError = keepStackTrace ? new Error('TimeoutError') : undefined
// eslint-disable-next-line no-async-promise-executor
return await new Promise(async (resolve, reject) => {
// Prepare the timeout timer
const timer = setTimeout(() => {
const err = new TimeoutError(`"${name || 'pTimeout function'}" timed out after ${timeout} ms`)
if (fakeError) err.stack = fakeError.stack // keep original stack
reject(err)
}, timeout)
// Execute the Function
try {
resolve(await promise)
} catch (err) {
reject(err)
} finally {
clearTimeout(timer)
}
})
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.