iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 13
1
Modern Web

你所不知道的各種前端 Debug 技巧系列 第 13

[Day 13] Sources - Step-by-Step Execution

利用斷點暫停程式碼後,就能控制程式碼的執行來進行更進一步的 Debug,筆者會在本篇文章介紹如何在 Sources 面板中進行 JavaScript Debugging。

概覽

使用 Sources 面板進行 Debug 的流程大致上可分為以下四個步驟

  • 重現問題 -- 找出產生 Bug 的流程,例如只要照著 A、B 的順序點擊按鈕就會出現問題。
  • 建立斷點 -- 確定 Bug 的產生方式後,選擇適當的斷點來快速找到問題程式碼的位置。
  • 控制執行 -- 暫停程式碼後,利用跳躍、逐步執行等方式控制程式碼的進行,觀察執行流程是否符合預期。
  • 檢查狀態 -- 逐步執行過程中可以監看變數、Call stack 的狀態,或是執行程式碼來檢查變數的值。

若還不熟悉斷點的建立方式,可以參考 Sources - Breakpoints,接下來的內容會包含控制執行以及檢查變數。
閱讀本文時建議搭配 Demo 頁面 Sources - Step-by-Step Execution,效果更佳。

 

執行控制

執行控制就像是程式碼的播放器,有暫停、繼續、跳至點擊處等功能,在控制的過程中觀察變數值的變化,藉此找出程式碼的問題。

當程式碼暫停執行時可以在程式碼的任意位置按下右鍵,選擇 Continue to Here 快轉到該處,略過沒有興趣的部分,當然也可以在該位置新增一個斷點並按下繼續,會有一樣的效果。

有時會想用一幀一幀慢慢播放的方式來看清影片的每個細節,而逐步執行就相當於播放影片時的「下個影格」功能。

 

逐步執行(Step)

面板上方可以很多個箭頭狀圖示,它們都叫做逐步執行,但在某些情況會有不同的行為。

 

Step over next function call

如果對即將執行的 Function 內部沒有興趣,Step over 會跳至該 Function 後方。

以下方程式碼為例,假設目前暫停在 A 行的 double

const number = 3;
const result = double(number); // A
console.log(result); // D

function double(n) {
  const result = n * 2; // B
  return result; // C
}

點擊圖示後會執行 double 內的所有程式碼並停在 D 行的 console.log

 

Step into next function call

如果即將執行的 Function 正是問題所在,Step into 會停在該 Function 內的第一行。

假設目前暫停在 A 行的 double

const number = 3;
const result = double(number); // A
console.log(result); // D

function double(n) {
  const result = n * 2; // B
  return result; // C
}

點擊圖示後會跳至 B 行,也就是 double 的第一行。

 

Step out of current function

確定目前所在的 Function 沒有問題時,Step out 會停在所處 Function 的後方。

假設目前暫停在 double 內的 B 行

const number = 3;
const result = double(number); // A
console.log(result); // D

function double(n) {
  const result = n * 2; // B
  return result; // C
}

點擊圖示後會執行完目前 Function 內剩下的程式碼,跳至 D 行的 console.log

 

非同步程式碼

在同步的狀況下 Step into next function callStep 的行為完全相同,遇到非 Built-in Function 就會進入,但非同步的狀況則有以下區別:

  • Step into -- 執行完同步的程式碼,進入非同步的程式碼
  • Step -- 跳至下一行同步的程式碼

以下方包含非同步行為的程式碼為例,直接執行的話數字為以 2、3、1 的順序印出,假設目前停在 A 行:

setTimeout(() => console.log(1), 2000); // A
console.log(2); // B
console.log(3); // C

按下 Step into 會進入 setTimeout 中的 Callback,也就是執行完 B、C 行,停在 D 行。

有注意到中間停了 2 秒才再次停止嗎?

按下 Step 會跳至 B 行,持續按下 Step 則會停在 C、D 行。

執行完 setTimeout 後已經過了 2 秒,console.log(1) 就直接執行了。

兩種 Step 的差別在於 Step into 為了進入 Function 內非同步的程式碼,需執行完同步的部分,而 step 則會停在下一行同步的程式碼,不直接進入非同步的部分。

 

多執行緒 (Threads)

當程式碼包含多執行緒時,Step intoStep 的行為也會有所區別。

以 Worker 開啟另一個執行緒為例子,假設目前停在 A 行:

// index.js
new Worker('worker.js'); // A
console.log('Hello World!'); // B

// worker.js
console.log('Hello Worker!'); // W

按下 Step into 會直接打開 worker.js 並停在 W 行,注意此時已經執行完 B 行了。

按下 Step 會跳至 B 行,再按一下 Step 會停在 W 行。

注意 Sources 面板的右上角會看到多個 Thread,Console 面板的 Context 也會被切換至 Worker。

Hello World!
Hello Worker!

 

不暫停

若想要暫時關閉斷點可以透過 Breakpoints 列表中的 Checkbox 開關各個斷點或點擊上方的 Deactivate breakpoints 關閉全部,另外可以在程式碼行號按下右鍵選擇 Never pause here 來避免 debugger 關鍵字觸發暫停。

 

多斷點

剛才的例子都以 A、B 等行號來標示暫停的位置,不過要注意在一行有多個 Function 的狀況下,可以分別在 function 前分別建立斷點。

function a() {
  console.log('a');
  return 1;
}
function b() {
  console.log('b')
  return 2;
}
console.log(a() + b());

在沒有建立斷點的情況下,Step over 會直接跳至下一行,在 b Function 前加入斷點或是使用 Step into 執行完 a Function 才能讓程式碼執行暫停在後方的 b Function 前。

 

小結

善用今天提到的跳轉、逐步執行功能,就能任意控制程式碼的執行來 Debug,相較不能中斷又很吃參數內容的 console.log 是不是強大許多呢?

不過除了逐步執行外,還有回溯、斷點管理等功能,下一篇文章將會繼續介紹 JavaScript Debugger 其他重要的功能。


上一篇
[Day 12] Sources - Breakpoints
下一篇
[Day 14] Sources - JavaScript Debugging
系列文
你所不知道的各種前端 Debug 技巧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
Dylan
iT邦新手 3 級 ‧ 2020-10-09 23:11:47

Step into 與 Step over 附的 GIF 圖好像反了

shizuku iT邦新手 5 級 ‧ 2020-10-10 03:00:19 檢舉

已修正,非常感謝你~

我要留言

立即登入留言