前言
javascript的中的異步是很重要的概念,特別是ajax的提出,給整個web帶來了很大的影響,今天就介紹下javascript的異步編程。
同步與異步
何為同步?何為異步呢?
同步:說白了就是程序一步一步從下向下執行,沒有什么別的代碼的跳動,就是按序執行,和在景區里女生上廁所是排隊是一樣的(每次女廁都是有好多人在排隊)。可以看成是一個單線程問題。
異步:異步就是程序可以跳着執行,開始執行一段程序之后不用等返回結果就執行其他的代碼,等結果返回之后在對結果進行處理,也就是可以在有限的時間內辦好幾件事情,提高效率,異步一般情況是多個線程問題。舉個例子,還是上廁所的例子,A說需要5分鍾搞定,B就可以5分鍾之后過來,B可以在這5分鍾干點別的事情。
所以說異步可以提高程序的執行效率,所以異步編程具有一定的好處,但是編寫異步程序卻不是那么容易的。
上面是我的理解,下面是摘自別人的文章:
"同步模式"就是上一段的模式,后一個任務等待前一個任務結束,然后再執行,程序的執行順序與任務的排列順序是一致的、同步的;"異步模式"則完全不同,每一個任務有一個或多個回調函數(callback),前一個任務結束后,不是執行后一個任務,而是執行回調函數,后一個任務則是不等前一個任務結束就執行,所以程序的執行順序與任務的排列順序是不一致的、異步的。
瀏覽器機制
我們都知道了,瀏覽器是單線程解析javascript的,也就是說,我們的瀏覽器原理是是不能搞成異步的,因為就一個線程嘛,如何搞成異步的呢?這就需要模擬異步來實現了,主要需要setTimeout函數。
setTimeout和setInterval
setTImeout是指延遲指定的毫秒數后調用函數或計算表達式。看個例子
setTimeout(function(){alert("延遲一秒彈出")},1000);
setInterval方法可按照指定的周期(以毫秒計)來調用函數或計算表達式,就是一定時間之后重新執行一次,例子
setInterval(function(){alert("一秒彈一次")},1000);
對應的取消方法是clearTimeout和clearInterval,用法很簡單。
用上面的兩個方法模擬所謂的“異步調用”事實上是通過將代碼段插入到代碼的執行隊列中實現的,因為javascript是單線程執行的,程序按序執行下來時,等瀏覽器有時間了就等一定的時間將setTimeout中的代碼添加到執行隊列中,通過例子說明吧
1 setTimeout(function(){console.log("延遲一秒打印我")},1000); 2 console.log("打印出我");
當執行1行代碼時,瀏覽器將里面的function(){console.log("延遲一秒打印我")}代碼添加到了執行隊列,並且延遲1秒之后執行,之后就到了第2行代碼,打印了結果"打印出我",過了一秒之后打印出"延遲一秒打印我"。可以看出第1行代碼執行了之后,並沒有執行完,而是執行了第2行代碼,1秒之后再執行setTimeout中的函數,就這樣模擬異步函數調用,其實說白了並不是真正的異步函數調用,事實上所謂的異步只是一個假象,同樣是運行在一個線程上。
假如現在在執行異步調用的時候,瀏覽器一直沒有時間,那么就不用進行調用了,例子
1 setTimeout(function(){console.log("延遲一秒打印我")},1000); 2 while(true){}//模擬一直很忙 3 console.log("打印出我");
上段代碼啥都不會打印出來,因為代碼function(){console.log("延遲一秒打印我")}插入之后,它要等1秒之后才能執行,但是瀏覽器一直都在忙,所以即使到了1秒鍾之后,代碼也不會被執行。
異步編程
一般來說,異步編程有四種方式,分別為回調函數方式,事件監聽方式,發布-訂閱方式和Promise方式,下面分別介紹這些方式。
回調函數
所謂回調函數,就是將函數作為參數傳到需要回調的函數內部再執行,·例子,例如f1是很耗時的程序,f1執行完之后執行f2,如果是普通的這樣
f1();
f2();
f2需要很長的時間才能執行,為了提高效率,我們使用回調函數的方式還模擬異步調用,這樣f1(f2),怎樣實現呢?其實就是利用setTimeout來實現
1 function f1(func){ 2 setTimeout(function(){ 3 //f1代碼 4 func(); 5 },0); 6 } 7 function f2(){} 8 f1(f2);
自己弄個例子吧
function f1(func){ setTimeout(function(){ for(var i=0;i<10000;i++){ console.log(i); } func(); },0); } function f2(){ console.log("f2"); } f1(f2); console.log("程序末尾");
結果:程序末尾 1 2 ...10000 f2
程序f1執行結束之后執行f2,f1執行的時候不影響后面程序的執行,所以先顯示“程序末尾”,然后是1.2.3.。。。。,最后是回調函數f2的結果。
ajax和nodejs主要就是通過回調函數這樣方式實現的異步調用,簡單介紹一下
ajax
1 var xmlhttp = new XMLHttpRequest(); 2 xmlhttp.open("POST","url", true); 3 xmlhttp.onreadystatechange = function(){ 4 if(xmlhttp.readyState == 4){ 5 if(xmlhttp.status == 200){ 6 console.log(xmlhttp.responseXML); 7 } 8 } 9 } 10 xmlhttp.send();
nodejs
1 fs.readdir(".", function (err, filenames) { 2 var i; 3 for (i = 0; i < filenames.length; i++) { 4 console.log(filenames[i]); 5 } 6 });
事件監聽
javascript的事件驅動模式是重要的概念,代碼的執行和順序沒有關系,而是與事件的何時發生有關系。javascript原生的綁定事件就是這個概念,例如
dom.onclick = function(){ console.log("click"); }
jquery就是類似這樣的,都是事件綁定,這是演示,
function f2(){} f1.on("complete",f2); f1.trigger("complete");
訂閱-發布
簡單說:兩種角色,訂閱者和發布者,訂閱者可以訂閱事件,發布者發布事件消息,發布者發布消息只能通知到訂閱改消息的訂閱者,未訂閱的不同接收到消息。專業解釋:存在一個"信號中心",某個任務執行完成,就向信號中心"發布"(publish)一個信號,其他任務可以向信號中心"訂閱"(subscribe)這個信號,從而知道什么時候自己可以開始執行。這就叫做"發布/訂閱模式"(publish-subscribe pattern),又稱"觀察者模式"(observer pattern)。
自己弄個訂閱-發布模式,大家看看,勿噴啊
1 (function(window,undefined){ 2 var topics = {}; 3 var queue = { 4 publish : function(topic,args){ 5 if(!topics[topic]){ 6 return; 7 } 8 var funcs = topics[topic], 9 len = funcs.length; 10 for(var i=0;i<len;i++){ 11 funcs[i](args); 12 } 13 return this; 14 }, 15 subscribe :function(topic,func){ 16 if(!topics[topic]){ 17 topics[topic] = []; 18 } 19 topics[topic].push(func); 20 } 21 } 22 window.AllenQueue = queue; 23 })(window);
用法:
AllenQueue.subscribe("done",function(){console.log("done")}); AllenQueue.publish("done")
其實我覺得時間監聽就是訂閱發布模式,這兩個應該是一樣的,事件機制就是訂閱發布的實例。
Promise
promise對象是CommonJS工作組提供的一種規范,用於異步編程的統一接口。promise對象通常實現一種then的方法,用來在注冊狀態發生改變時作為對應的回調函數。promise模式在任何時刻都處於以下三種狀態之一:未完成(unfulfilled)、已完成(resolved)和拒絕(rejected)。以CommonJS Promise/A 標准為例,promise對象上的then方法負責添加針對已完成和拒絕狀態下的處理函數。then方法會返回另一個promise對象,以便於形成promise管道,這種返回promise對象的方式能夠支持開發人員把異步操作串聯起來,如then(resolvedHandler, rejectedHandler)。resolvedHandler 回調函數在promise對象進入完成狀態時會觸發,並傳遞結果;rejectedHandler函數會在拒絕狀態下調用。
Jquery在1.5的版本中引入了一個新的概念叫Deferred,就是CommonJS promise A標准的一種衍生。可以在jQuery中創建$.Deferref的對象。同時也對發送ajax請求以及數據類型有了新的修改,參考JQuery API。
例子如下
1 function f1(){ 2 var dfd = $.Deferred(); 3 setTimeout(function () { 4 // f1的任務代碼 5 dfd.resolve(); 6 }, 500); 7 return dfd.promise; 8 } 9 f1().then(f2)
這個暫時就這樣,以后我們會單獨詳細說說這個promise的。
小結
異步調用是javascript中一個重要的部分,我們還是要好好的掌握的。