引言
JavaScript 的獨特功能透過回呼函式以非同步方式運作。指派非同步回呼可讓您編寫事件導向的程式碼,但也會由於 JavaScript 無法以線性方式執行,因此也會追蹤髮型錯誤。
幸好,現在您可以在 Chrome 開發人員工具中查看非同步 JavaScript 回呼的完整呼叫堆疊!
在開發人員工具中啟用非同步呼叫堆疊功能後,您就能在不同時間點深入瞭解網頁應用程式的狀態。針對某些事件監聽器、setInterval
、setTimeout
、XMLHttpRequest
、承諾、requestAnimationFrame
、MutationObservers
等,進行完整的堆疊追蹤。
行走堆疊追蹤時,您也可以分析執行階段執行作業該特定時間點的任何變數值。就像手錶運算式的時光機一樣!
我們來啟用這項功能,並看看下列幾種情況。
在 Chrome 中啟用非同步偵錯功能
在 Chrome 中啟用這項新功能,即可試用這項新功能。前往 Chrome Canary 開發人員工具的「來源」面板。
右側的「Call Stack」面板旁邊有一個新的「Async」核取方塊。切換核取方塊,開啟或關閉非同步偵錯功能 (不過在開啟後,您可能不希望將其關閉)。
擷取延遲計時器事件和 XHR 回應
您或許曾在 Gmail 中看過以下內容:
如果傳送要求時發生問題 (可能是伺服器發生問題或用戶端發生網路連線問題),Gmail 會在短暫逾時後自動嘗試重新傳送郵件。
如要瞭解非同步呼叫堆疊如何協助分析延遲計時器事件和 XHR 回應,我已使用 Gmail 模擬範例重新建立該流程。上方的連結中有完整的 JavaScript 程式碼,但其流程如下:
光是查看舊版開發人員工具中的「Call Stack」面板,postOnFail()
中的中斷點可讓您瞭解從何處呼叫 postOnFail()
。但是,請看看開啟非同步堆疊後的差異:
啟用非同步呼叫堆疊後,您就可以查看整個呼叫堆疊,輕鬆查看要求是從 submitHandler()
(按一下提交按鈕後發生),還是從 retrySubmit()
(發生在 setTimeout()
延遲之後發生):
以非同步方式查看運算式
當您步行完整呼叫堆疊時,已觀察的運算式也會更新,反映當下的狀態!
根據過去範圍評估程式碼
除了單純查看運算式以外,您也可以直接在開發人員工具 JavaScript 控制檯面板中與先前範圍的程式碼互動。
假設您是博士,需要一點幫助,才能比較你進入 Tardis 前的時鐘和「現在時間」。透過開發人員工具控制台,您可以輕鬆評估、儲存及計算不同執行階段的值。
繼續使用開發人員工具操控運算式,就不必回想原始碼、進行編輯和重新整理瀏覽器。
拆解的承諾解決方案
如果您認為先前的模擬 Gmail 流程在未啟用非同步呼叫堆疊功能的情況下難以展開,您能想像一下,因為鏈結承諾等較為複雜的非同步流程會讓流程更加困難嗎?讓我們回顧 Jake Archibald 教學課程中的 JavaScript Promise 教學課程最終範例。
以下的動畫展示在 Jake 的 async-best-example.html 範例中,追蹤呼叫堆疊。
深入瞭解網頁動畫
讓我們深入瞭解 HTML5Rocks 封存檔案。還記得 Paul Lewis 的 Leaner、Meaner、Fast Animations 和 requestAnimationFrame 嗎?
開啟 requestAnimationFrame 示範,然後在 post.html 的 update() 方法 (約第 874 行) 的開頭加入中斷點。透過非同步呼叫堆疊,我們可更深入瞭解 requestAnimationFrame,包括逐步返回啟動捲動事件回呼。
使用 MutationObserver 時追蹤 DOM 更新
MutationObserver
可讓我們觀察 DOM 中的變化。在這個簡易範例中,點選按鈕後,系統就會將新的 DOM 節點附加至 <div class="rows"></div>
。
在 demo.html 的 nodeAdded()
(第 31 行) 中新增中斷點。啟用非同步呼叫堆疊後,您現在可以透過 addNode()
將呼叫堆疊返回至初始點擊事件。
非同步呼叫堆疊中的 JavaScript 偵錯提示
為函式命名
如果您想將所有回呼指派為匿名函式,可能會想改為命名,以便查看呼叫堆疊。
例如,使用這樣的匿名函式:
window.addEventListener('load', function() {
// do something
});
並將其命名為 windowLoaded()
:
window.addEventListener('load', function <strong>windowLoaded</strong>(){
// do something
});
載入事件觸發時,會在開發人員工具堆疊追蹤中顯示其函式名稱,而非隱密的「(匿名函式)」。方便您快速掌握堆疊追蹤中的活動。
深入探索
總結來說,以下是開發人員工具會顯示完整呼叫堆疊的所有非同步回呼:
- 計時器:返回
setTimeout()
或setInterval()
的初始化位置。 - XHR:返回呼叫
xhr.send()
的位置。 - 動畫頁框:返回呼叫
requestAnimationFrame
的位置。 - Promise: 返回已解決承諾的問題。
- Object.observe:返回觀察器回呼最初繫結的位置。
- MutationObservers:請回頭返回發生異動觀察器事件的觸發位置。
- window.postMessage():處理內部程序的訊息呼叫。
- DataTransferItem.getAsString()
- FileSystem API
- IndexedDB
- WebSQL
- 透過
addEventListener()
取得符合資格的 DOM 事件:返回觸發事件的位置。基於效能考量,並非所有 DOM 事件都符合非同步呼叫堆疊功能的資格。目前可用的事件範例包括:「scroll」、「hashchange」和「selectionchange」。 - 透過
addEventListener()
進行的多媒體事件:返回觸發事件的位置。可用的多媒體事件包括:音訊和影片事件 (例如「play」、「pause」、「ratechange」)、WebRTC MediaStreamTrackList 事件 (例如「addtrack」、「removetrack」) 和 MediaSource 事件 (例如「sourceopen」)。
能夠查看 JavaScript 回呼的完整堆疊追蹤,建議您將這些頭髮留在頭上。當多個非同步事件彼此相關,或在非同步回呼中擲回未偵測到的例外狀況時,開發人員工具中的這項功能特別實用。
在 Chrome 中試試看。 如果您對這項新功能有任何意見,歡迎在 Chrome 開發人員工具錯誤追蹤工具或 Chrome 開發人員工具群組上留言。