function a(){} function b(){} a(); b();
以上爲同步代碼,函數b
必須等函數a
執行完畢後才能執行。javascript
function a(){ setTimeout(function(){ b(); }, 1000); }; function c(){}; a(); c();
首先執行函數a
,並且不等setTimeout
執行就執行函數c
,等待至少1s的時候後纔會執行函數b
.實際上在是等待了1s後將函數b
放到了event queue
裏面,此時要等待主線程空閒的時候,纔會取event queue
裏面等待的回調函數
進行執行。html
以上是一段簡單的異步代碼,js裏面最基礎的異步實現就是調用setTimeout
,setInterval
。java
關於js的異步實現請看下面的list:
談談javascript的異步實現ajax
回調函數:在js裏面簡單點來講,就是函數被看成參數傳入另一個函數當中,並在那個函數中被調用。編程
var b = function (){ //執行相關的代碼 } var a = function (b){ //執行相關的代碼 b(); } a(b);
你們可能平時聽的比較多的是異步回調
,可是必須搞清楚,異步與回調並無直接的聯繫,回調只是異步的一種實現方式。
固然還有同步回調,即上面回調部分舉的簡單的例子。通常使用回調函數主要是將父函數的執行結果通知給回調函數進行處理。segmentfault
關於異步個人理解是:瀏覽器
由於js是單線程的,若是全部的操做(如ajax操做,獲取遠程的js文件等IO操做)是同步的,遇到那些耗時的操做,後面的程序必然被阻塞不能執行,頁面也就失去了響應。所以js採用了事件驅動機制,在單線程模型下,使用異步回調函數的方式來實現非阻塞的IO操做。併發
異步任務
是指js在主線程(stack
)運行的過程中,當stack
空閒的時候,主線程對event queque
輪詢(事實上一直在輪詢)後,將異步任務放到stack
裏面進行執行。簡單點說,只要指定過回調函數,那麼當這些事件發生的時候就會進入事件隊列
,等待主線程的stack
空閒的時候,就會對event queue
裏面的回調讀取並放到stack裏面執行。異步
看一段ajax實現的代碼:async
var xhr = new XMLHttpRequest(); xhr.open('POST', url, true); //第三個參數決定是否採用異步的方式 xhr.send(data); xhr.onreadystatechange = function(){ if(xhr.readystate === 4 && xhr.status === 200){ ///xxxx } }
這裏ajax請求是異步
的,由於瀏覽器會新開一個線程請求,當請求的狀態(readystate)
發生改變,由於以前就設置了回調函數,每次狀態發生改變都會調用相應的回調函數,當(xhr.readystate === 4 && xhr.status === 200)
的時候,回調函數進入了event queue
,等待主線程空閒的時候,而且event queue
裏面排在這個回調前面沒有其餘回調的時候就會獲得執行。
異步回調產生的結果就是,函數的調用並不直接返回結果,而每每是交給回調函數進行異步處理。
所以在異步編程當中,須要注意幾個地方:
須要把依賴於異步函數(須要其執行結果或者達到某種狀態)的代碼放在對應的回調函數中(例如上面的ajax的例子)
異步函數後面的代碼會當即執行(所以須要知道某段代碼是否爲異步的)
另外還有一個關於script標籤異步加載的內容:
你們記得請求遠程腳本標籤嗎?
<script src='xxxx' async></script>
在script標籤裏面加入了async屬性或者defer屬性後,一樣變成了異步了。
關於這部分的內容,請移步:
async和defer的區別
另外關於這部分的內容還有一些List:
併發模型與event loop
樸靈評阮老師的event loop