javascript非同步程式設計幾種方法簡介

admin發表於2017-02-11
由於javascript是單執行緒的,也就是說一次只能夠完成執行一次任務,其他的任務都要在後面排隊,前面的任務完成後才能夠繼續進行第二個任務。這個就如銀行只有一個辦理業務的視窗,大家必須排隊等候。這種模式雖然簡單,但是也存在諸多的弊端,會導致程式執行事件過長,有的時候還會導致瀏覽器卡死等現象。為了解決這個問題,js將任務執行模式分為兩種,一種是同步模式(Synchronous)、一種是非同步模式(Asynchronous)

    “同步模式”就是上一段的模式,後一個任務等待前一個任務結束,然後再執行,程式的執行順序與任務的排列順序是一致的、同步的;“非同步模式”則完全不同,每一個任務有一個或多個回撥函式(callback),前一個任務結束後,不是執行後一個任務,而是執行回撥函式,後一個任務則是不等前一個任務結束就執行,所以程式的執行順序與任務的排列順序是不一致的、非同步的。

a:3:{s:3:\"pic\";s:43:\"portal/201702/11/142637m5d1i57rz8941r7k.png\";s:5:\"thumb\";s:0:\"\";s:6:\"remote\";N;}

 “非同步模式”非常重要。在瀏覽器端,耗時很長的操作都應該非同步執行,避免瀏覽器失去響應,最好的例子就是Ajax操作。在伺服器端,“非同步模式”甚至是唯一的模式,因為執行環境是單執行緒的,如果允許同步執行所有http請求,伺服器效能會急劇下降,很快就會失去響應。本文總結了“非同步模式”程式設計的4種方法,理解它們可以讓你寫出結構更合理、效能更出色、維護更方便的Javascript程式。

一.回撥函式:

使用回撥函式是非同步程式設計的最基本的方法。

例如有兩個函式fun1和fun2,後者在等待前者的執行結果。

[JavaScript] 純文字檢視 複製程式碼
fun1();
fun2();

如果fun1的執行時間很長,那麼就可以考慮把函式fun2作為fun1的回撥函式執行,將fun1改造如下:

[JavaScript] 純文字檢視 複製程式碼
function fun1(callback){
  setTimeout(function(){
    // fun1的任務程式碼
    callback();
  }, 1000);
}

於是程式碼執行就變成如下形式:

[JavaScript] 純文字檢視 複製程式碼
fun1(fun2);

採用此種方式,就把同步操作變成了非同步操作,函式fun1並不能夠阻塞fun2的執行。

總結:回撥函式的優點是簡單、容易理解和部署,缺點是不利於程式碼的閱讀和維護,各個部分之間高度耦合(Coupling),流程會很混亂,而且每個任務只能指定一個回撥函式。

二.事件監聽:

採用事件驅動模式,這樣任務的執行不取決於程式碼的順序,而是取決於事件的發生順序。下面看程式碼解析:

為fun1繫結一個事件(採用jQuery方式):

[JavaScript] 純文字檢視 複製程式碼
fun1.on(‘done’, fun2);

上面這行程式碼的意思是,當fun1發生done事件,就執行fun2。然後,對fun1進行改寫:

[JavaScript] 純文字檢視 複製程式碼
function fun1(){
  setTimeout(function () {
    // fun1的任務程式碼
    fun1.trigger(‘done’);
  }, 1000);
}

fun1.trigger(‘done’)表示,執行完成後,立即觸發done事件,從而開始執行fun2。

這種方法的優點是比較容易理解,可以繫結多個事件,每個事件可以指定多個回撥函式,而且可以“去耦合”(Decoupling),有利於實現模組化。缺點是整個程式都要變成事件驅動型,執行流程會變得很不清晰。

三.釋出/訂閱:

在第二種中的“事件”,完全可以理解為“訊號”。

假定,存在這樣的一個“訊號中心”,某個任務執行完成,就向訊號中心“釋出”(publish)一個訊號,其他任務可以向訊號中心“訂閱”(subscribe)這個訊號,從而知道什麼時候自己可以開始執行。這就叫做“釋出/訂閱模式”(publish-subscribe pattern),又稱“觀察者模式”(observer pattern)。

這個模式有多種實現,下面採用的是Ben Alman的Tiny Pub/Sub,這是jQuery的一個外掛。

首先,f2un向“訊號中心”jQuery訂閱“done”訊號。

[JavaScript] 純文字檢視 複製程式碼
jQuery.subscribe(“done”, fun2)

然後,fun1進行如下改寫:

[JavaScript] 純文字檢視 複製程式碼
function fun1(){
   setTimeout(function () {
     // fun1的任務程式碼
     jQuery.publish(“done”);
   }, 1000);
}

jQuery.publish(“done”)的意思是,fun1執行完成後,向“訊號中心”jQuery釋出“done”訊號,從而引發fun2的執行。

此外,fun2完成執行後,也可以取消訂閱(unsubscribe)。

jQuery.unsubscribe(“done”, fun2);

這種方法的性質與“事件監聽”類似,但是明顯優於後者。因為我們可以通過檢視“訊息中心”,瞭解存在多少訊號、每個訊號有多少訂閱者,從而監控程式的執行。

四.Promises物件:

Promises物件是CommonJS工作組提出的一種規範,目的是為非同步程式設計提供統一介面。

簡單說,它的思想是,每一個非同步任務返回一個Promise物件,該物件有一個then方法,允許指定回撥函式。比如,fun1的回撥函式fun2,可以寫成:

[JavaScript] 純文字檢視 複製程式碼
fun1().then(fun2);

fun1要進行如下改寫(這裡使用的是jQuery的實現):

[JavaScript] 純文字檢視 複製程式碼
function f1(){
var dfd = $.Deferred();
    setTimeout(function () {
      // f1的任務程式碼
      dfd.resolve();
    }, 500);
   return dfd.promise;
}

這樣寫的優點在於,回撥函式變成了鏈式寫法,程式的流程可以看得很清楚,而且有一整套的配套方法,可以實現許多強大的功能。

比如,指定多個回撥函式:

[JavaScript] 純文字檢視 複製程式碼
fun1().then(fun2).then(fun3);

再比如,指定發生錯誤時的回撥函式:

[JavaScript] 純文字檢視 複製程式碼
fun1().then(fun2).fail(fun3);

相關文章