iT邦幫忙

2023 iThome 鐵人賽

DAY 2
0
自我挑戰組

JS 加強筆記系列 第 2

Day 02:從 callbacks 開始

  • 分享至 

  • xImage
  •  

今天開始 promise 的章節,會想讀這部分除了自己對它理解很差之外,覺得 promise 的世界有太多理所當然和習慣成自然,常常有類似這樣的討論。promise 有很多彈性和方案,但肯定不會所有事情總是習慣問題,希望寫完這段後可以更有自信的做出判斷。不過,在真的寫到 promise 前還要先寫一點 callbacks 作為前情提要。

callback

JS 的同步與非同步本身就是很大的主題,這裡暫時就不特別延伸,主要的重點會放在如何處理非同步。基本的概念是:JS 的同步特性照著順序一次做一件事情,但有的時候這樣可能會有點問題 (事情花太多時間或想要延遲執行...等等)。因此 JS 中有非同步的行為,讓事情可以「現在開始,但晚點完成」,就可以正常做其他事並在事情完成時得到正確結果。

例如下方是一個用來載入檔案的 loadScript 函式,它做的事情是產生一個 script 標籤,並以傳入的參數作為 src 屬性值,接著把標籤放入文檔。然後瀏覽器就會非同步載入檔案 [註1],並在載入後開始執行:

function loadScript(src) {
    let script = document.createElement('script');
    script.src = src;
    document.head.append(script);
}

// 呼叫 loadScript
loadScript('/my/script.js');

因為是非同步,代表 loadScript 執行完後檔案在背景載入,loadScript 底下的程式碼會繼續依序執行,不會等它完成。

也就是說,如果像下方,執行 loadScript 後想馬上調用檔案中的某個函式就會失敗:

loadScript('/my/script.js'); // 假設檔案中有 newFunction 這個函式
newFunction(); // X 沒有這個函式

於是這時候自然會有一個需求:我們想知道什麼時候可以調用檔案裡的東西。但是透過此時此刻的 loadScript 無法知道檔案何時載完,只知道它終究會載入執行而已。

所以稍微修改一下,讓 loadScript 再傳入一個 callback 參數,裡面包含檔案載完之後想要做的事情,接著監聽 script 在 onload 時執行 callback,這樣就能確保正確取到檔案裡的東西:

function loadScript(src, callback) {
    let script = document.createElement('script');
    script.src = src;

    script.onload = () => callback(script);

    document.head.append(script);
}

// 呼叫 loadScript
loadScript('/my/script.js', function() {
    // 這個 callback 在 script 載完後執行,就可以使用 newFunction()
    newFunction();
});

這樣的做法是以 callback 來處理非同步行為 (“callback-based” asynchronous programming),也就是非同步任務的函式會提供一個 callback 參數,用來執行務完成後想要做的事 [註2]。

換個角度或許可以形容成,非同步任務讓程式不會塞車卡死,但因為只有任務裡面才知道做完了沒,所以把要做的事一起打包送進去,才能正確進行。有一段時間這種 callback 作法是處理 JS 非同步的主要方式,後來不再是主流當然是因為它有一些缺點,接下來會寫到這個部分。

[註1] 當初盯著「非同步載入檔案」這句話很久,雖然好像蠻合理的,但我一直在想為什麼它一定就是非同步,後來 (竟然是在同一份文件) 看到這句 'Dynamic scripts behave as “async” by default.'。script 標籤的各種行為也是很值得做成筆記...

[註2] 「等特定事情發生,再呼叫 callback」的作法很像 JS 的事件處理,這段對於 JS 事件監聽和非同步有更多敘述。

另外也讀了一下這篇問答中對於非同步程式的敘述。


上一篇
Day 01:到底哪裡需要加強
下一篇
Day 03:callbacks 的錯誤處理及問題
系列文
JS 加強筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言