国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

V8 的 Error 對象與棧追蹤的妙用

Luosunce / 594人閱讀

摘要:現(xiàn)狀最近在寫歡迎的時候,一直為錯誤的棧追蹤而愁。由于送入隊列的是函數(shù),因此在的參數(shù)可以放心地使用。其次,這些函數(shù)并不是立即在中調(diào)用的,而是由專門的隊列處理代碼來調(diào)用。

本文的講述都是以 Node.js 環(huán)境為例子,而 Node.js 使用的 JavaScript 引擎是 V8,因此理論上 Chrome 也能適用,其它瀏覽器我就不清楚了。

現(xiàn)狀

最近在寫 Rize(歡迎 star) 的時候,一直為錯誤的棧追蹤而愁。為什么呢?這要從 Rize 的架構(gòu)說起。

由于 puppeteer 的絕大多數(shù)操作和 API 是異步的,而寫異步代碼的良好寫法是用 ES2017 的 async/await 語法。

但我們都知道,async/await 實際上返回的是一個 Promise(即使你沒有顯式地 return 什么,它將是 Promise)。很明顯這樣不能達(dá)到我想要的 API 鏈?zhǔn)秸{(diào)用的效果。我總不能對著 Promise 實例操作 prototype,然后把我自己的 API 挪上去吧?

所以我使用了一個隊列來保存用戶想要進(jìn)行的操作。也就是說,用戶在調(diào)用 Rize 的 API 之后,并不會(也不可能)立即執(zhí)行這些操作,而是放在隊列中,等待時機適合(例如瀏覽器已經(jīng)啟動或者上一個操作已經(jīng)完成)才執(zhí)行。由于送入隊列的是函數(shù),因此在 push 的參數(shù)可以放心地使用 async/await。

但是,一旦這些操作中出現(xiàn)錯誤,錯誤的定位變得十分麻煩。

下面這張圖是直接用 Node.js 運行一個腳本的結(jié)果:

下面這張圖是在 Jest 中執(zhí)行一段代碼的結(jié)果:

原因是,

首先,隊列中的函數(shù)是 async function,這本來就給 debug 帶來麻煩。

其次,這些函數(shù)并不是立即在 API 中調(diào)用的,而是由專門的隊列處理代碼來調(diào)用。在錯誤發(fā)生時,V8 只能跟蹤到那段隊列處理代碼那里。

這就為用戶帶來麻煩。錯誤發(fā)生了,卻只能看著錯誤消息一點一點地去試著定位有問題的地方。

探索

為此我去閱讀了 Node.js 的官方文檔,看了 Errors 這一部分,不過似乎沒什么收獲。

后來又找到了 TJ Holowaychuk 大神寫的庫 callsite,看看能不能有用。從文檔上看,這個庫并不適合我的需求。

但我閱讀了 callsite 的源碼,源碼很短,十行不到。我在源碼發(fā)現(xiàn)了一些信息。

callsite 是利用 V8 的 Stack Trace API 來獲取函數(shù)調(diào)用處的一些信息,如文件名,行號等等。callsite 是如何獲取這些數(shù)據(jù)的呢?

非常簡單,就一句:

var err = new Error()

對,僅僅是 new 一個 Error 實例,而且并不是要拋出這個錯誤。

對比我們平時的代碼,通常當(dāng)我們 throw 一個錯誤之后,我們能得到一些錯誤棧信息。但實際上,不需要 throw,僅僅是新建一個 Error 實例,也能讓 V8 記錄下當(dāng)前的調(diào)用棧信息。

解決

既然發(fā)現(xiàn)這個事實,那我們可以在需要記錄調(diào)用棧的地方 new 一個 Error 實例。(千萬不要把它拋出,不然你后面的代碼就沒法執(zhí)行了)

此時當(dāng)前的棧信息已經(jīng)被記錄下來,那么我們怎樣去使用這些信息呢?

如果用戶的代碼執(zhí)行正常,那就沒什么關(guān)系了。關(guān)鍵是在發(fā)生錯誤的時候。這里要提一提的是,我的那段隊列處理代碼是帶有 try…catch 塊的,大概長這樣:

try {
  await fn()
} catch (error) {
  throw error
} finally {
  // do some stuff ...
}

你可能好奇什么要把捕捉的異常還要拋出,因為我想要的是后面的 finally 塊啊,但同時我又希望異常能繼續(xù)被拋出。

在這里,我們就要對 catch 塊做點功夫。當(dāng)然這個 try…catch 塊是能夠獲取到之前新建的 Error 實例的,在這里我省略了那部分代碼。

為了方便敘述,我把之前 new 的那個 Error 實例命名為 trace,即假設(shè) const trace = new Error()。

顯然把 trace 的所有棧信息都拿過來是不適合的,因為它有一些我們并不需要的棧信息(這部分信息是位于 API 調(diào)用處以上的)。

每一個 Error 實例都有個 stack 屬性,它是一個多行字符串,我們先把它的每行分開,保存在數(shù)組中:

const stack = trace.stack!.split("
")

要注意 stack 的第一行不是棧信息,而是錯誤消息,這個不能去掉。所以:

stack.splice(1, 2)

我這里有兩行的信息是沒用的,所以刪去兩行,實際上要根據(jù)你的需要修改第二個參數(shù)。

現(xiàn)在可以把 trace 的棧信息替換掉實際 error 的棧信息:

error.stack = stack.join("
")
結(jié)果

現(xiàn)在就可以得到友好的錯誤棧信息了:

配合 Jest 就能更好地定位問題所在之處:

最后是宣傳一下我正在寫的庫 Rize(可以讓你簡單優(yōu)雅地使用 puppeteer),也就是本文提到的,歡迎前往 GitHub 并 star。

博客原文在這里

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/93303.html

相關(guān)文章

  • JavaScript工作原理(一):引擎,運行時,調(diào)用堆棧

    摘要:調(diào)用棧是一種單線程編程語言,這意味著它只有一個調(diào)用棧。這就是調(diào)用棧的功能。簡單代碼示例當(dāng)引擎執(zhí)行這段代碼時,調(diào)用棧為空,之后運行如下每個叫做堆棧幀。調(diào)用棧就是通過堆棧幀來追蹤異常,堆棧幀基本就是調(diào)用棧出現(xiàn)異常時候的狀態(tài)。 概述 幾乎每個人都已經(jīng)聽說過V8引擎這個概念,而且大多人都知道JavaScript是單線程的,并且使用回調(diào)隊列。 這篇文章中,我們將詳細(xì)介紹這些概念,并解釋JavaS...

    Jingbin_ 評論0 收藏0
  • 【前端進(jìn)階之路】內(nèi)存基本知識

    摘要:在運行腳本時,需要顯示的指定對象。大對象區(qū)每一個區(qū)域都是由一組內(nèi)存頁構(gòu)成的。這里是唯一擁有執(zhí)行權(quán)限的內(nèi)存區(qū)。換句話說,是該對象被之后所能回收到內(nèi)存的總和。一旦活躍對象已被移出,則在舊的半空間中剩下的任何死亡對象被丟棄。 內(nèi)存管理 本文以V8為背景 對之前的文章進(jìn)行重新編輯,內(nèi)容做了很多的調(diào)整,使其具有邏輯更加緊湊,內(nèi)容更加全面。 1. 基礎(chǔ)概念 1.1 生命周期 不管什么程序語言,內(nèi)存...

    Simon_Zhou 評論0 收藏0
  • JavaScript是如何工作:引擎,運行時間以及回調(diào)概述

    摘要:是如何工作的引擎,運行時以及調(diào)用棧的概述原文譯者隨著變得越來越流行,團(tuán)隊在多個層級都對它進(jìn)行利用前端,后端,混合應(yīng)用,嵌入式設(shè)備以及更多。這個將會在是如何工作的的第二部分進(jìn)一步解釋。 How JavaScript works: an overview of the engine, the runtime, and the call stack JavaScript是如何工作的:引擎,運...

    he_xd 評論0 收藏0
  • 【譯】JavaScript 如何工作:對引擎、運行時、調(diào)用堆棧概述

    摘要:調(diào)用棧是一種數(shù)據(jù)結(jié)構(gòu),它記錄了我們在程序中的位置。當(dāng)從這個函數(shù)返回的時候,就會將這個函數(shù)從棧頂彈出,這就是調(diào)用棧做的事情。而且這不是唯一的問題,一旦你的瀏覽器開始處理調(diào)用棧中的眾多任務(wù),它可能會停止響應(yīng)相當(dāng)長一段時間。 原文地址: https://blog.sessionstack.com... PS: 好久沒寫東西了,最近一直在準(zhǔn)備寫一個自己的博客,最后一些技術(shù)方向已經(jīng)敲定了,又可以...

    Warren 評論0 收藏0
  • JavaScript 工作原理之一-引擎,運行時,調(diào)用堆棧(譯)

    摘要:本章會對語言引擎,運行時,調(diào)用棧做一個概述。調(diào)用棧只是一個單線程的編程語言,這意味著它只有一個調(diào)用棧。查看如下代碼當(dāng)引擎開始執(zhí)行這段代碼的時候,調(diào)用棧會被清空。之后,產(chǎn)生如下步驟調(diào)用棧中的每個入口被稱為堆棧結(jié)構(gòu)。 原文請查閱這里,本文采用知識共享署名 4.0 國際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原...

    Betta 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<