JavaScript 非同步程式設計

JavaScript 非同步程式設計

非同步的概念

非同步(Asynchronous, async)是與同步(Synchronous, sync)相對的概念。

在我們學習的傳統單執行緒程式設計中,程式的執行是同步的(同步不意味著所有步驟同時執行,而是指步驟在一個控制流序列中按順序執行)。而非同步的概念則是不保證同步的概念,也就是說,一個非同步過程的執行將不再與原有的序列有順序關係。

簡單來理解就是:同步按你的程式碼順序執行,非同步不按照程式碼順序執行,非同步的執行效率更高。

以上是關於非同步的概念的解釋,接下來我們通俗地解釋一下非同步:非同步就是從主執行緒發射一個子執行緒來完成任務。

什麼時候用非同步程式設計

在前端程式設計中(甚至後端有時也是這樣),我們在處理一些簡短、快速的操作時,例如計算 1 + 1 的結果,往往在主執行緒中就可以完成。主執行緒作為一個執行緒,不能夠同時接受多方面的請求。所以,當一個事件沒有結束時,介面將無法處理其他請求。

現在有一個按鈕,如果我們設定它的 onclick 事件為一個死迴圈,那麼當這個按鈕按下,整個網頁將失去響應。

為了避免這種情況的發生,我們常常用子執行緒來完成一些可能消耗時間足夠長以至於被使用者察覺的事情,比如讀取一個大檔案或者發出一個網路請求。因為子執行緒獨立於主執行緒,所以即使出現阻塞也不會影響主執行緒的執行。但是子執行緒有一個侷限:一旦發射了以後就會與主執行緒失去同步,我們無法確定它的結束,如果結束之後需要處理一些事情,比如處理來自伺服器的資訊,我們是無法將它合併到主執行緒中去的。

為了解決這個問題,JavaScript 中的非同步操作函式往往通過回撥函式來實現非同步任務的結果處理。

回撥函式

回撥函式就是一個函式,它是在我們啟動一個非同步任務的時候就告訴它:等你完成了這個任務之後要幹什麼。這樣一來主執行緒幾乎不用關心非同步任務的狀態了,他自己會善始善終。

例項

function print() { document.getElementById("demo").innerHTML="itread01!"; } setTimeout(print, 3000);

嘗試一下 ?

這段程式中的 setTimeout 就是一個消耗時間較長(3 秒)的過程,它的第一個引數是個回撥函式,第二個引數是毫秒數,這個函式執行之後會產生一個子執行緒,子執行緒會等待 3 秒,然後執行回撥函式 "print",在命令列輸出 "Time out"。

當然,JavaScript 語法十分友好,我們不必單獨定義一個函式 print ,我們常常將上面的程式寫成:

例項

setTimeout(function () { document.getElementById("demo").innerHTML="itread01!"; }, 3000);

嘗試一下 ?

注意:既然 setTimeout 會在子執行緒中等待 3 秒,在 setTimeout 函式執行之後主執行緒並沒有停止,所以:

例項

setTimeout(function () { document.getElementById("demo1").innerHTML="itread01-1!"; }, 3000); document.getElementById("demo2").innerHTML="itread01-2!"; console.log("2");

嘗試一下 ?

這段程式的執行結果是:

itread01-1!
itread01-2!

非同步 AJAX

除了 setTimeout 函式以外,非同步回撥廣泛應用於 AJAX 程式設計。有關於 AJAX 詳細請參見:https://www.itread01.com/ajax/ajax-tutorial.html

XMLHttpRequest 常常用於請求來自遠端伺服器上的 XML 或 JSON 資料。一個標準的 XMLHttpRequest 物件往往包含多個回撥:

例項

var xhr = new XMLHttpRequest(); xhr.onload = function () { // 輸出接收到的文字資料 document.getElementById("demo").innerHTML=xhr.responseText; } xhr.onerror = function () { document.getElementById("demo").innerHTML="請求出錯"; } // 傳送非同步 GET 請求 xhr.open("GET", "https://www.itread01.com/try/ajax/ajax_info.txt", true); xhr.send();

嘗試一下 ?

XMLHttpRequest 的 onload 和 onerror 屬性都是函式,分別在它請求成功和請求失敗時被呼叫。如果你使用完整的 jQuery 庫,也可以更加優雅的使用非同步 AJAX:

例項

$.get("https://www.itread01.com/try/ajax/demo_test.php",function(data,status){ alert("資料: " + data + "\n狀態: " + status); });

嘗試一下 ?