亚洲国产日韩欧美一区二区三区,精品亚洲国产成人av在线,国产99视频精品免视看7,99国产精品久久久久久久成人热,欧美日韩亚洲国产综合乱

js異步編程

原創(chuàng) 2016-11-11 13:28:35 407
摘要:前言以一個煮飯的例子開始,例如有三件事,A是買菜、B是買肉、C是洗米,最終的結(jié)果是為了煮一餐飯。為了最后一餐飯,可以三件事一起做,也可以輪流做,也可能C需要最后做(等A、B做完),這三件事是相關(guān)的,抽象起來有三種場景。順序做先買菜,再買肉,最后洗米, 即 A->B->C。并發(fā)做買菜,買肉,洗米,一起做。交集做買菜,買肉必須先做完,才能做洗米。場景就是這樣,接下來就是如何考慮用js實現(xiàn)。

前言

以一個煮飯的例子開始,例如有三件事,A是買菜、B是買肉、C是洗米,最終的結(jié)果是為了煮一餐飯。為了最后一餐飯,可以三件事一起做,也可以輪流做,也可能C需要最后做(等A、B做完),這三件事是相關(guān)的,抽象起來有三種場景。

順序做

先買菜,再買肉,最后洗米, 即 A->B->C。

1.png

并發(fā)做

買菜,買肉,洗米,一起做。

2.png

交集做

買菜,買肉必須先做完,才能做洗米。

3.png

場景就是這樣,接下來就是如何考慮用js實現(xiàn)。

function A(callback){
    setTimeout(function(){
          callback("買好菜了");
     }, 1000);
}function B(callback){
     setTimeout(function(){
          callback("買好肉了");
     }, 2000);
}function C(callback){
     setTimeout(function(){
          callback("洗米好了");
     }, 3000);
}

普通做法(無任何模式)

順序做

A(function(res){
     console.log(res);
     B(function(res){
          console.log(res);
          C(function(res){
               console.log(res);
               console.log("煮飯啦~~~");
          });
     });
});

這就是傳說中的回調(diào)地獄,實際的場景嵌套的層級可能還更多。

并發(fā)做

var cook_count = 0;
A(function(res){
     callback(res);
});
B(function(res){
     callback(res);
});
C(function(res){
     callback(res);
});function callback(res){
     console.log(res);
     cook_count++;     if(cook_count == 3){
           console.log("煮飯啦~~~");
     }
}

設(shè)置一個計數(shù)器,記錄事情發(fā)展,到計數(shù)到指定的值,即為結(jié)束。

交集做

交集做也可以使用計數(shù)器做。

var cook_count = 0;
A(function(res){
     callback(res);
});
B(function(res){
     callback(res);
});function callback(res){
     console.log(res);
     cook_count++;     if(cook_count == 2){
     C(function(res){
         console.log(res);
         console.log("煮飯啦~~~");
     });
     }
}

發(fā)布/訂閱模式

我們可以用這種模式來解決js異步編程。

順序做

function A(){
  setTimeout(function(){
    pubsub.publish("A_FINISH", "買好菜了");
  }, 1000);
}function B(){
  setTimeout(function(){
    pubsub.publish("B_FINISH", "買好肉了");
  }, 2000);
}function C(){
  setTimeout(function(){
    pubsub.publish("C_FINISH", "洗米好了");
  }, 3000);
}
A();
pubsub.subscribe("A_FINISH",  B);
pubsub.subscribe("B_FINISH",  C);
pubsub.subscribe("C_FINISH", function(){
  console.log("煮飯啦~~~");
});

PS:還有一直很常用的jquery的自定義事件也是類似的寫法(on、trigger)。

并發(fā)做

訂閱/發(fā)布并沒有真正優(yōu)化到異步編程,所以并發(fā)做其實還要用其他方式去檢查事情完成狀態(tài),就如之前的計數(shù)。

var cook_count = 0;
pubsub.subscribe("A_FINISH",  callback);
pubsub.subscribe("B_FINISH",  callback);
pubsub.subscribe("C_FINISH",  callback);function callback(res){
     console.log(res);
     cook_count++;     if(cook_count == 3){
           console.log("煮飯啦~~~");
     }
}

PS:交集做也是類似,就不舉例了。

Promise/Deferred模式

ES6規(guī)范包含了Promise,Promise對象可以理解為一次將要執(zhí)行的操作(常常被用于異步操作),使用了 Promise 對象之后可以用一種鏈式調(diào)用的方式來組織代碼,讓代碼更加直觀。。

順序做

A().then(function(res) {
    console.log(res);    return B();
}).then(function(res) {
    console.log(res);    return C();
}).then(function(res) {
    console.log(res);
    console.log("煮飯啦~~~");
});function A() {    return new Promise(function (resolve, reject) {
         setTimeout(function(){
            resolve("買好菜了");
        }, 1000);
    });
}function B() {    return new Promise(function (resolve, reject) {
          setTimeout(function(){
               resolve("買好肉了");
          }, 2000);
    });
}function C() {    return new Promise(function (resolve, reject) {
          setTimeout(function(){
               resolve("洗米好了");
          }, 2000);
    });
}

then方法定義:then(fulfilledAHandler, errorHandler),對應(yīng)著Promise的resolve, reject。

并發(fā)做

Promise.all方法可做并發(fā)調(diào)用,當所有Promise 對象都變?yōu)橥瓿蓱B(tài)或失敗態(tài)時,回調(diào)將被執(zhí)行。

Promise.all([A(), B(), C()]).then(function (result) {
    console.log(result); //["買好菜了", "買好肉了", "洗米好了"]});

第三方框架實現(xiàn)

jQuery之Deferred

Deferred對象(jQuery的回調(diào)函數(shù)解決方案),對異步編程做了優(yōu)化,有人總結(jié)四種功能。

一、 提供ajax操作的鏈式寫法

$.ajax("test.html")
    .done(function(){ alert("哈哈,成功了!"); })
    .fail(function(){ alert("出錯啦!"); });

PS:高于1.5.0版本,返回的是deferred對象,可以進行鏈式操作。

二、同一操作的多個回調(diào)函數(shù)

$.ajax("test.html")
     .done(function(){ alert("哈哈,成功了!");} )
     .done(function(){ alert("第二個回調(diào)函數(shù)!");} );

三、為多個操作指定回調(diào)函數(shù)

$.when($.ajax("test1.html"), $.ajax("test2.html"))
     .done(function(){ alert("哈哈,成功了!"); })
     .fail(function(){ alert("出錯啦!"); });

PS:如果都成功了,就運行done()指定的回調(diào)函數(shù);如果有一個失敗或都失敗了,就執(zhí)行fail()指定的回調(diào)函數(shù)。

四、普通操作的回調(diào)函數(shù)接口

$(function(){   var A = function(){ //并不是ajax
     var dtd = $.Deferred();
       setTimeout(function(){
          dtd.resolve("買好菜了");
       }, 1000);
      return dtd.promise(); // 返回promise對象   }; 
    var B = function(){
      var dtd = $.Deferred();
        setTimeout(function(){
            dtd.resolve("買好肉了");
        }, 2000);
      return dtd.promise(); // 返回promise對象   }; 
    var C = function(){
      var dtd = $.Deferred();
        setTimeout(function(){
            dtd.resolve("洗米好了");
        }, 2000);
      return dtd.promise(); // 返回promise對象   };
   $.when(A(), B(), C())
    .done(function(v1, v2, v3){  console.log("煮飯啦~~~", v1 , v2, v3); })
    .fail(function(){ alert("出錯啦!"); });
});

PS:deferred.promise()方法。它的作用是,在原來的deferred對象上返回另一個deferred對象,后者只開放與改變執(zhí)行狀態(tài)無關(guān)的方法(比如done()方法和fail()方法),屏蔽與改變執(zhí)行狀態(tài)有關(guān)的方法(比如resolve()方法和reject()方法),從而使得執(zhí)行狀態(tài)不能被改變。

總結(jié)

發(fā)布/訂閱模式相對算是一種較為原始的方式,Promise/Deferred模式貢獻了一個非常不錯的異步任務(wù)模型的抽象,而實際上我們可能會常用的是第三方庫來做異步編程,畢竟這些比較成熟及方便。另外如果有其他更好的異步編程方式請回復(fù)我一下,讓我可以進行總結(jié)。


發(fā)佈手記

熱門詞條