jQuery的Deferred機制

2018-07-24 10:48 更新

Deferred 對象是在 jQuery1.5 中引入的回調(diào)管理對象。其作用,大概就是把一堆函數(shù)按順序放入一個調(diào)用鏈,然后根據(jù)狀態(tài),來依次調(diào)用這些函數(shù)。 AJAX 的所有操作都是使用它來進(jìn)行封裝的。比如我們定義的,當(dāng)請求正常返回時,會調(diào)用 success 定義的函數(shù),失敗時,會調(diào)用 error定義的函數(shù)。這里的“失敗”,“正?!本褪菭顟B(tài),而對應(yīng)的函數(shù),只是調(diào)用鏈中的一個而且。

先來看一個直觀的例子:

var obj = $.Deferred(function(a){});
obj.done(function(){console.log('1')});
obj.done(function(){console.log('2')});
obj.resolve();

這樣,我們就可以按順序看到 1 , 2 這兩個輸出了。

總的來說, jQuery 的 Deferred 對象有三個狀態(tài): done , fail , process 。

  • process 只能先于其它兩個狀態(tài)先被激發(fā)。
  • done 和 fail 互斥,只能激發(fā)一個。
  • process 可以被重復(fù)激發(fā),而 done 和 fail 只能激發(fā)一次。

然后, jQuery 提供了一些函數(shù)用于添加回調(diào),激發(fā)狀態(tài)等:

deferred.done()
添加一個或多個成功回調(diào)。
deferred.fail()
添加一個或多個失敗回調(diào)。
deferred.always()
添加一個函數(shù),同時應(yīng)用于成功和失敗。
deferred.progress()
添加一個函數(shù)用于準(zhǔn)備回調(diào)。
deferred.then()
依次接受三個函數(shù),分別用于成功,失敗,準(zhǔn)備狀態(tài)。
deferred.reject()
激發(fā)失敗狀態(tài)。
deferred.resolve()
激發(fā)成功狀態(tài)。
deferred.notify()
激發(fā)準(zhǔn)備狀態(tài)。

如果一個 Deferred 已經(jīng)被激發(fā),則新添加的對應(yīng)的函數(shù)會被立即執(zhí)行。

除了上面的這些操作函數(shù)之外, jQuery 還提供了一個 jQuery.when() 的回調(diào)管理函數(shù),可以用于方便地管理多個事件并發(fā)的情況,先看一個 AJAX 的“原始狀態(tài)”例子:

var defer = $.ajax({
  url: '/json.html',
  dataType: 'json'
});

defer.done(function(data){console.log(data)});

.done() 做的事和使用 success 定義是一樣的。

當(dāng)我們需要完成,像“請求A和請求B都完成時,執(zhí)行函數(shù)”之類的需求時,使用 $.when() 就可以了:

var defer_1 = $.ajax({
  url: '/json.html',
  dataType: 'json'
});

var defer_2 = $.ajax({
  url: '/jsonp.html',
  dataType: 'jsonp'
});

var new_defer = $.when(defer_1, defer_2);
new_defer.done(function(){console.log('haha')});

在 $.when() 中的 Deferred ,只要有一個是 fail ,則整體結(jié)果為 fail 。

Deferred 的回調(diào)函數(shù)的執(zhí)行順序與它們的添加順序一致。

這里特別注意一點,就是 done / fail / always 與 then 的返回值的區(qū)別。從功能上看,它們都可以添加回調(diào)函數(shù),但是,方法的返回值是不同的。 前組的返回值是原來的那個 defer 對象,而 then 返回的是一個新的 defer 對象。

then 返回新的 defer 這種形式,可以用于方便地實現(xiàn)異步函數(shù)的鏈?zhǔn)秸{(diào)用。

比如對于:

var defer = $.ajax({
    url: '/json',
    dataType: 'json'
});

如果使用 done 方法:

defer.done(function(){
  return $.ajax({
    url: '/json',
    dataType: 'json',
    success: function(){
      console.log('inner')
    }
  });
}).done(function(){
  console.log('here');
});

等同于是調(diào)用了兩次 defer.done , defer.done ,注冊的兩次回調(diào)函數(shù)依次被執(zhí)行后,我們看到的輸出是:

here
inner

這是兩次 defer.done 的結(jié)果,第一個回調(diào)函數(shù)返回了一個新的 defer 沒任何作用。

如果換成 then 方法的話:

defer.then(function(){
  return $.ajax({
    url: '/json',
    dataType: 'json',
    success: function(){
      console.log('inner')
    }
  });
}).done(function(){
  console.log('here');
});

上面的代碼相當(dāng)于:

var new_defer = defer.then(...);
new_defer.done(...);

它跟兩次 defer.done 是不同的。 new_defer 會在 inner 那里的 defer 被觸發(fā)時再被觸發(fā),所以輸出結(jié)果是:

inner
here

更一般地來說 then 的行為,就是前面的注冊函數(shù)的返回值,會作為后面注冊函數(shù)的參數(shù)值:

var defer = $.ajax({
  url: '/json',
  dataType: 'json'
});

defer.then(function(res){
  console.log(res);
  return 1;
}).then(function(res){
  console.log(res);
  return 2;
}).then(function(res){
  console.log(res);
});

上面代碼的輸入結(jié)果是:

ajax response
1
2


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號