[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 單線程