[JavaScript] 非同步(Asynchronus) 函式

在介紹 非同步(Asynchronus) 能幫我們什麼事前,我們直接用編碼來呈現 非同步(Asynchronus) 函式,來理解 非同步(Asynchronus) 是如何運行的,與其原理:


我們直接使用下面的編碼進行說明:

window.setTimeout(function(){
 console.log('兩秒執行')
},2000)

console.log('執行')

我們知道,程式碼在運行是,都是從上往下執行的,照這個邏輯,執行的過程會如下:

過程:

等待 2 秒 ......
console.log('兩秒執行')
console.log('執行')

期望結果:

兩秒執行
執行

結果卻不是上面所預期的 ,我們真正執行的結果是:

執行後結果:

執行
兩秒執行

其實我們在使用 setTimeout() 來執行我們要操作的事件時,你可以把他想像成,有 另外 一隻 子程式 另外在運行,如圖示:

注意圖中的編號,下面將依序說明執行的過程:


Step 1 :

我們在 主程式 這邊先呼叫 setTimeout() 的函式,此時系統產生出了一個 子程式 在背景 運行


Step 2 :

我們在 主程式 在 呼叫完 setTimeout() 的函式後,就 不去理會 setTimeout() 裡運行的程式了,裡面的東西通通交給 子程式 處理,所以繼續往下執行

直接執行 console.log('執行') 的程式碼


Step 3 :

子程式 監控完 2 秒的時間

執行 console.log("兩秒執行")

結束子程式

希望到目前為止的說明,可以讓你對 非同步(Asynchronus) 函式 的執行過程 有所理解,這是一個很重要的概念


AJAX

另外在 上一篇文章 [JavaScript] AJAX 簡介:實作篇 所提到的 AJAX ,其 Asynchronus(非同步) JavaScript and XML 中的 非同步 指的也是相同的概念

我們來看編碼:

var req = new XMLHttpRequest();

req.open('get','http://skyroxas.tw/aboutme');

req.onload = function(){
  console.log('連線執行')
}

req.send(); //執行連線 //注意 send() 函數

console.log("執行");

XMLHttpRequest() 物件裡所提供的 send() 函式,也跟上面舉例的 setTimeout() 的方法運作模式相同

皆會在 呼叫完 函式後,繼續執行 主程式,而將 AJAX 內要執行的函式交給後台中的 子程式 處理

到這邊也又對 AJAX 的概念又進一步理解了吧


為什麼需要 非同步(Asynchronus) 函式 的函式:

如果你已經了解 非同步(Asynchronus) 函式 的執行流程了,應該就會有感覺了

如果我們以 同步(Synchronous (單線程) , 來執行下面的函式:

一個完成換下一個,下一個換成在換下下一個處理

setTimeout(function(){
 console.log('9999999 微秒後執行')
},9999999)

console.log('執行') // 要等完 9999999 微秒後,才可以執行

非同步(Asynchronus) 函式 , 就是為了解決上述的問題所提出來的

非同步為每一個任務都有一個 CallBack,代表每個任務執行完後會執行該CallBack而不是執行下一個任務,因此程式執行順序與任務的排列順序是不相同的


如果 程式 都是 依序 執行 就會存在個很大的問題,很容易就產生 程式在執行時的阻塞

並且 網路延遲並非人人都一樣,光是這點可能就會使 程式在運行中產生很大的變數

等待的時間是很難預測的,我們如果等到這段代碼執行完後,才可以繼續執行後面的代碼,會造成下列這些問題:

  • 使用者在請求資料時,畫面有很大的機率會是不完整的,因為相關的程式碼都還在等待資料的回傳,才可以執行,即使其他的編碼跟請求回來的資料並不相關
  • 如果使用者在請求資料時發生錯誤,那後面的代碼就卡住了,畫面將會慘不忍睹

非同步(Asynchronus) 函式 解決了上述的問題,採用 「事件驅動(Event Driven)」的模式,因為我們 無法預測等待的時間,不如執行後,先放著讓他跑,先去做其他的事

等他 處理好後再通知 , 在執行相關的編碼就可以了


參考連結:JavaScript 單線程

Facebook 功能: