在最後這個星期要開始進入同步與非同步的課題了,在進入語法部分,例如常用的Promise
、async/await
之前,我們要先理解非同步函式是如何被執行。
這篇文章會整理以下知識:
XMLhttprequest
我們都知道JavaScript是單執行緒,意思是它「一次只能做一件事」,就像一心不能二用,要把一件事做完才做下一件事。但按此原則,當執行時遇上setTimeout
、XMLhttprequest
這些需要時間執行的情況時,就會導致阻塞,逼使我們一直乾等它們完成才可以往下跑其他程式碼,那麼下面的功能(例如點擊按鈕、表單功能等等)基本上都是報廢了。
因此,我們需要一個運作方法去處理這些耗時的程式,那就是非同步的執行方式。非同步的方式就是先把這些會阻塞運作的程式,先移到一個叫「事件佇列」的地方,過某個時段後,才回傳它並且執行它。
具體例子:
console.log(1); //1
//setTimeout是需要非同步執行,所以先不執行
setTimeout(function cb(){
console.log(3)}, 3000) //3
console.log(2) //2
有一點要注意,並非所有回傳函式會是非同步,回傳函式可以是同步,所以並非所有回傳函式都會被移到事件佇列,同步的回傳函式是可以直接在呼叫堆疊被執行掉。
所以我們現在知道,那些耗時的程式會被採用非同步的方式執行。而同步程式就會直接在呼叫堆疊被執行掉。實際流程如下:
注意:
JS遇到函式時,會把它推進呼叫堆疊,執行完後就推走。
Web API是瀏覽器本身有提供的API。例如是:
getElementById
setTimeout
、setInterval
XMLHttpRequest
loupe這個網站具體模擬了上面的運作情景,它更具體地描繪了整個非同步執行的過程。
當你看完loupe預設例子的動畫後,你會留意到第一行$.on('button', 'click',...)
會一直留在Web APIs裏,因為它是在等待點擊事件,只要點擊沒有觸發,它就會一直留在直留在Web APIs裏等待,不會移到事件佇列,甚至最後移到呼叫堆疊裏被執行。
這就是為什麼大部分DOM元素都是設計成非同步執行,因為它們通常都需要等待事件被觸發,才會回傳函式,並執行函式。
Event loop(事件迴圈)負責把事件佇列裏的函式推回呼叫堆疊執行。它會檢查呼叫堆疊是否已經被清空,如果是,就會依序把儲存在事件佇列(Event queue)的函式推到呼叫堆疊,並且執行它們。
XMLhttprequest
除了setTimeout
這類計時器的例子外,AJAX技術也是常常會用到非同步的執行方式。AJAX技術透過使用XMLhttprequest
物件,使我們不用刷新畫面,也能與伺服器互動,包括提出請求,以及回傳資料。
因為抓取資料是需要時間,也會因應網絡狀況而延長,所以使用XMLhttprequest
時,我們可以在第三個參數設定true
,意思是用非同步方式取得遠端資料。
範例如下:
const xhr = new XMLHttpRequest();
xhr.open('get','https://randomuser.me/api/',true);
xhr.send(null);
xhr.onload = function(){
console.log('執行次序:2') //執行次序:2
console.log(xhr.responseText) //取得遠端資料
}
console.log('執行次序:1') //執行次序:1
圖解:
同步程式:
非同步(異步)程式:
XMLhttprequest
取得資料的速度是因應網絡及伺服器的狀應而定)明天會繼續整理有關非同步常用語法的概念與運用方法,感謝你的閱讀~ 還沒跑完鐵人賽的朋友,我們一起加油!!
異步程式設計與事件迴圈
JS 原力覺醒 Day13 - Event Queue & Event Loop 、Event Table
一次只能做一件事情的 JavaScript,解釋 Event queue 怎能不用動畫呢
JavaScript's Call Stack, Callback Queue, and Event Loop