【javascript 進階】異步調用


前言

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中一個重要的部分,我們還是要好好的掌握的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM