簡體   English   中英

如何使用承諾查看 setTimeout 中錯誤的完整堆棧跟蹤

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM