jQuery.deferred對(duì)象使用詳解

字號(hào):


    這篇文章主要為大家詳細(xì)介紹了jQuery.deferred對(duì)象的使用方法,內(nèi)容很全面,感興趣的小伙伴們可以參考一下
    一、前言
    jQuery1.5之前,如果需要多次Ajax操作,我們一般會(huì)使用下面的兩種方式:
    1).串行調(diào)用Ajax
    $.ajax({ success: function() { 
      $.ajax({ success: function() {
        $.ajax({ //callbacks... 
            }); 
      }); 
    });  
    這種方式代碼可讀性差,效率低,晦澀難懂,調(diào)試和排錯(cuò)的復(fù)雜度大。
    2).并行調(diào)用Ajax
    var promises = []; 
    $.ajax({
      success: function() { 
        promises.push('resolved'); 
        check(); 
      } 
    }); 
    $.ajax({ 
      success: function() { 
        promises.push('resolved'); 
        check();
      } 
    }); 
    $.ajax({ 
      success: function() { 
        promises.push('resolved'); 
        check(); 
      } 
    }); 
    var check = function() { //checks for all 3 values in the promises array }
    這種方式對(duì)于callbacks函數(shù)調(diào)用來(lái)說(shuō)已經(jīng)很不錯(cuò)了,并行取得數(shù)據(jù),可讀性良好。缺點(diǎn)就是代碼冗長(zhǎng),可擴(kuò)展性差,調(diào)試和排錯(cuò)的復(fù)雜度高。
    jQuery1.5之后,增加了deferred對(duì)象。因此可以用下面這種方式實(shí)現(xiàn)和上面同樣的需求。
    1)Promise
    var address = $.ajax({}); 
    var tweets = $.ajax({}); 
    var facebook = $.ajax({}); 
    render_side_bar = function(address, tweets, facebook){ 
      //render sidebar 
    }
    render_no_side_bar = function () { }
    $.when(address, tweets, facebook).then(render_side_bar, render_no_side_bar)
    可以看出,代碼可讀性良好,可擴(kuò)展性高,并且大大降低了調(diào)試和排錯(cuò)的復(fù)雜度。
    那么問(wèn)題來(lái)了,promises和deferred對(duì)象究竟是個(gè)什么玩意呢?
    二、詳解
    2.什么是deferred對(duì)象?
    deferred對(duì)象即延遲對(duì)象,它是jQuery 1.5版本引入的一種回調(diào)函數(shù)的解決方案,代表了將要完成的某種操作,并且提供了一些方法,幫助用戶使用。
    deferred對(duì)象是對(duì)Promises接口的實(shí)現(xiàn)。jQuery 1.5版本以及之后所有的Ajax返回的jqXHR對(duì)象就是一個(gè)deferred對(duì)象。
    2.deferred對(duì)象的幾大好處
    2.1.為同一操作指定多個(gè)回調(diào)函數(shù)
    deferred對(duì)象的好處之一,就是它允許你為一個(gè)操作添加多個(gè)回調(diào)函數(shù),這在傳統(tǒng)的ajax中是無(wú)法實(shí)現(xiàn)的。
    $.ajax("test.html")
    .done(function(){ alert("first success callback!");} )
    .fail(function(){ alert("there is an error!"); } )
    .done(function(){ alert("second success callback!");} );
    2.2.為多個(gè)操作指定同一個(gè)回調(diào)函數(shù)
    deferred對(duì)象的好處之二,就是它允許你為多個(gè)操作指定同一個(gè)回調(diào)函數(shù),這在傳統(tǒng)的ajax中也是無(wú)法實(shí)現(xiàn)的。
    $.when($.ajax({}), $.ajax({}))
    .done(function(){ alert("success!"); })
    .fail(function(){ alert("error!"); });
    2.3.非Ajax操作的回調(diào)函數(shù)
    deferred對(duì)象的好處之三,就是它不再拘泥于ajax操作,任意的操作(ajax操作or本地操作/異步操作or同步操作)都可以使用deferred對(duì)象,指定回調(diào)函數(shù)。
    一個(gè)很典型的耗時(shí)操作
    var dfd = $.Deferred(); // create a deferred object
    var wait = function(dtd){
     var tasks = function(){
     alert("over!");
     dtd.resolve(); // change the state of the deferred object from pending to resolved
     };
     setTimeout(tasks,50000);
     return dtd;
    };
    $.when(wait(dtd))
    .done(function(){ alert("success!"); })
    .fail(function(){ alert("error!"); });
    2.4.鏈?zhǔn)秸{(diào)用
    jQuery中傳統(tǒng)的ajax操作是這樣的:
    $.ajax({
      url: "",
    success: function(){
     alert("success!");
    },
    error:function(){
     alert("error!");
    }
    });
    其中success指定ajax操作成功后的回調(diào)函數(shù),error指定ajax操作失敗后的回調(diào)函數(shù)。jQuery1.5版本之前,Ajax操作返回的是一個(gè)XMLHTTPRequest對(duì)象,不支持鏈?zhǔn)讲僮鳌?.5版本開(kāi)始,ajax操作返回的是jqXHR對(duì)象,這是一個(gè)deferred對(duì)象,而deferred對(duì)象一個(gè)顯著的好處就是可以進(jìn)行鏈?zhǔn)讲僮?,因?yàn)閐eferred對(duì)象的所有方法返回的均是deferred對(duì)象。
    現(xiàn)在的ajax操作的寫(xiě)法是:
    $.ajax({})
    .done(function(){ alert("success!"); })
    .fail(function(){ alert("fail!"); });
    兩種寫(xiě)法對(duì)比可以很明顯的看出來(lái),done()相當(dāng)于傳統(tǒng)ajax操作的success方法,fail()相當(dāng)于傳統(tǒng)ajax操作的fail方法。相對(duì)于傳統(tǒng)的寫(xiě)法,代碼可讀性提高了。
    3.deferred對(duì)象的方法
    3.1基本用法
    (1).生成deferred對(duì)象
    var dfd = $.Deferred(); //create a deferred object
    (2).deferred對(duì)象的狀態(tài)
    deferred對(duì)象有三種狀態(tài)
    pending:表示操作處于未完成的狀態(tài),任何deferred(延遲)對(duì)象開(kāi)始于pending狀態(tài)。
    resolved:表示操作成功。
    rejected:表示操作失敗。
    state()方法返回deferred對(duì)象的當(dāng)前狀態(tài)。
    $.Deferred().state(); // 'pending'
    $.Deferred().resolve().state(); // 'resolved'
    $.Deferred().reject().state(); // 'rejected'
    (3).改變deferred對(duì)象的狀態(tài)
    調(diào)用deferred.resolve() 或者 deferred.resolveWith()轉(zhuǎn)換Deferred(遞延)到resolved(解決)的狀態(tài),并立即執(zhí)行設(shè)置中任何的doneCallbacks。
    var callbackFunc = function(){console.log(arguments[0]);}
    var dfd = $.Deferred();
    dfd.done(callbackFunc);
    dfd.resolve("hello"); //'hello'
    調(diào)用deferred.reject() 或者 deferred.rejectWith()轉(zhuǎn)換Deferred(遞延)到rejected(拒絕)的狀態(tài),并立即執(zhí)行設(shè)置中任何的failCallbacks。
    var callbackFunc = function(){console.log(arguments[0]);}
    var dfd = $.Deferred();
    dfd.fail(callbackFunc);
    dfd.reject("fail"); //'fail'
    (4).綁定回調(diào)函數(shù)
    deferred對(duì)象狀態(tài)改變的時(shí)候,會(huì)觸發(fā)回調(diào)函數(shù)。任何回調(diào)使用deferred.then(), deferred.always(), deferred.done()或者 deferred.fail()添加到這個(gè)對(duì)象都是排隊(duì)等待執(zhí)行。
    pending-->resolved,執(zhí)行設(shè)置中任何的doneCallbacks(done()指定),參數(shù)由resolved傳遞給doneCallbacks。
    pending-->rejected,執(zhí)行設(shè)置中任何的failCallbacks(fail()指定),參數(shù)由resolved傳遞給failCallbacks。
    pending-->resolved/rejected,執(zhí)行always()指定的callbacks,參數(shù)由resolved傳遞給callbacks。
    var f1 = function(){console.log("done");}, 
       f2 = function(){console.log("fail");}, 
       f3 = function(){console.log("always");};
    var dfd = $.Deferred();
    dfd.done(f1).fail(f2).always(f3);
    //if
    dfd.resolve(); //'done' 'always'
    //if
    dfd.reject(); //'fail' 'always'
    如果在狀態(tài)更改后附件一個(gè)callback則會(huì)立即執(zhí)行callback,因此不必?fù)?dān)心deferred對(duì)象何時(shí)被resolved或者rejected,因?yàn)闊o(wú)論何時(shí),參數(shù)都會(huì)正確地傳遞給callbacks。
    var fun1 = function(){console.log(arguments[0]);},
      fun1 = function(){console.log(arguments[0]);};
    var dfd = $.Deferred();
    dfd.done(fun1);
    dfd.resolve("hello"); //'hello'
    dfd.done(fun2); //'hello'
    3.2.deferred對(duì)象的方法
    (1)$.Deferred([beforeStart]) -- 創(chuàng)建一個(gè)deferred對(duì)象,參數(shù)類(lèi)型為Function,是一個(gè)在構(gòu)造函數(shù)之前調(diào)用的函數(shù)。
    var func = function(){console.log("start");} 
    var dfd = $.Deferred(func); //'start' create a deferred object
    (2)deferred.done(doneCallbacks [,doneCallbacks]) -- 當(dāng)deferred(延遲)對(duì)象解決時(shí),調(diào)用添加處理程序。
    args:接受一個(gè)或者多個(gè)參數(shù),所有的參數(shù)都可以是一個(gè)單一的函數(shù)或者函數(shù)數(shù)組,當(dāng)deferred(延遲)對(duì)象解決時(shí),doneCallbacks被調(diào)用?;卣{(diào)是依照他們添加的順序執(zhí)行的。
    var func1 = function(){console.log("1");},
       func2 = function(){console.log("2");},
       func3 = function(){console.log("3");};
    var dfd = $.Deferred();
    dfd.done([func1,func2],func3,[func2,func1]);
    dfd.resolve(); // "1 2 3 2 1"
    (3)deferred.fail(failCallbacks [,failCallbacks]) -- 當(dāng)deferred(延遲)對(duì)象拒絕時(shí),調(diào)用添加處理程序。
    args:接受一個(gè)或者多個(gè)參數(shù),所有的參數(shù)都可以是一個(gè)單一的函數(shù)或者函數(shù)數(shù)組,當(dāng)deferred(延遲)對(duì)象拒絕時(shí),failCallbacks被調(diào)用?;卣{(diào)是依照他們添加的順序執(zhí)行的。
    var func1 = function(){console.log("1");},
       func2 = function(){console.log("2");},
       func3 = function(){console.log("3");};
    var dfd = $.Deferred();
    dfd.fail([func1,func2],func3,[func2,func1]);
    dfd.reject(); // "1 2 3 2 1"
    (4)deferred.resolve(args) and deferred.resolveWith(context [,args]) -- 解決Deferred(延遲)對(duì)象,并根據(jù)給定的args參數(shù)(resolveWith給定context)調(diào)用任何doneCallbacks。
    參數(shù):args -- type(object),傳遞給回調(diào)函數(shù)(doneCallbacks)的可選的參數(shù),
    context -- type(object),Context(上下文)作為this對(duì)象傳遞給完成回調(diào)函數(shù)(doneCallbacks)。
    var func = function(arg){console.log(arg);};
    $.Deferred().done(func).resolve("done!"); //'done!'
    var func = function(arg1,arg2){console.log(arg1.name + ',' + arg2);};
    $.Deferred().done(func).resolve({name:'Lucy'},'How are you!'); // 'Lucy,How are you!'
    resolve和resolveWith的區(qū)別就等同于fire和fireWith的區(qū)別。
    var func = function () {
      console.log(this.name + ',' + arguments[0] + ' ' + arguments[1] + ' ' + arguments[2]);
    };
    $.Deferred().done(func).resolveWith({ name: "Lucy" }, ["How", "are", "you!"]);//'Lucy,How are you!'
    (5)deferred.reject(args) and deferred.rejectWith(context [,args]) -- 拒絕Deferred(延遲)對(duì)象,并根據(jù)給定的args參數(shù)(rejectWith給定context)調(diào)用任何failCallbacks。
    參數(shù):args -- type(object),傳遞給回調(diào)函數(shù)(doneCallbacks)的可選的參數(shù),
    context -- type(object),Context(上下文)作為this對(duì)象傳遞給完成回調(diào)函數(shù)(doneCallbacks)。
    var func = function(arg){console.log(arg);};
    $.Deferred().fail(func).reject("error!"); //'error!'
    var func = function(ctx,arg){console.log(ctx.name + ',' + arg);};
    $.Deferred().fail(func).reject({name:'Mark'},'What happend!'); // 'Mark,What happend!'
    reject和rejectWith的區(qū)別就等同于fire和fireWith的區(qū)別。
    var func = function () {
      console.log(this.name + ',' + arguments[0] + ' ' + arguments[1]);
    };
    $.Deferred().fail(func).rejectWith({ name: "Mark" }, ["what", "happend!"]); // 'Mark,What happend!'
    (6)deferred.promise([target]) -- 返回Deferred(延遲)的Promise(承諾)對(duì)象。
     參數(shù)可選,無(wú)參數(shù)時(shí)返回一個(gè)Promise(承諾)對(duì)象,Promise(承諾)對(duì)象僅會(huì)暴露那些需要綁定額外的處理或判斷狀態(tài)的延遲方法(then, done, fail, always,pipe, progress, state,和 promise)時(shí),并不會(huì)暴露任何用于改變狀態(tài)的延遲方法(resolve, reject, notify,resolveWith, rejectWith, 和 notifyWith)。使用Promise(承諾)會(huì)阻止其他人破壞你制造的promise。
    function asyncEvent() {
       var dfd = jQuery.Deferred();
        // Resolve after a random interval
        setTimeout(function () {
           dfd.resolve("hurray");
        }, Math.floor(400 + Math.random() * 2000));
        // Reject after a random interval
        setTimeout(function () {
           dfd.reject("sorry");
        }, Math.floor(400 + Math.random() * 2000));
        // Show a "working..." message every half-second
        setTimeout(function working() {
           if (dfd.state() === "pending") {
              dfd.notify("working... ");
               setTimeout(working, 500);
            }
         }, 1);
          // Return the Promise so caller can't change the Deferred
          return dfd.promise();
     }
    // Attach a done, fail, and progress handler for the asyncEvent
    $.when(asyncEvent()).then(
        function (status) {
           alert(status + ", things are going well");
        },
        function (status) {
           alert(status + ", you fail this time");
        },
        function (status) {
           alert(status);
        }
    );
    有參數(shù)時(shí),會(huì)將事件綁定到參數(shù)上,然后返回該參數(shù)對(duì)象(返回的實(shí)際是一個(gè)擴(kuò)展的Promise(承諾)對(duì)象)。
    var obj = {
      hello: function (name) {
        alert("Hello " + name);
      }
    },
    // Create a Deferred
    dfd = $.Deferred();
    // Set object as a promise
    dfd.promise(obj);
    // Resolve the deferred
    dfd.resolve("John");
    // Use the object as a Promise
    obj.done(function (name) {
       obj.hello(name); // will alert "Hello John"
    }).hello("Karl");
    (7)$.when(deferreds) -- 提供一種方法來(lái)執(zhí)行一個(gè)或多個(gè)對(duì)象的回調(diào)函數(shù)。
    參數(shù):type(Deferred),一個(gè)或多個(gè)延遲對(duì)象,或者普通的JavaScript對(duì)象。
    參數(shù)僅傳入一個(gè)單獨(dú)的Deferred對(duì)象,返回它的Promise對(duì)象。
    function func() {
      var dfd = $.Deferred();
      setTimeout(function () {
        dfd.resolve("hurry");
      }, 500);
      return dfd.promise();
    };
    $.when(func()).done(function (arg) {
      alert(arg); /*alert "hurry"*/
    });
    參數(shù)傳入一個(gè)非Deferred和Promise對(duì)象,那么該參數(shù)會(huì)被當(dāng)成一個(gè)被解決(resolved)的延遲對(duì)象,并且綁定到上面的任何doneCallbacks都會(huì)被立即執(zhí)行。
    $.when( { name: 123 } ).done(
      function(arg) { alert(arg.name); } /* alerts "123" */
    );
    無(wú)參數(shù),返回一個(gè)resolved(解決)狀態(tài)的Promise對(duì)象。
    $.when().state(); // "resolved"
    參數(shù)為多個(gè)Deferred對(duì)象,該方法根據(jù)一個(gè)新的“宿主” Deferred(延遲)對(duì)象,跟蹤所有已通過(guò)Deferreds聚集狀態(tài),返回一個(gè)Promise對(duì)象。當(dāng)所有的延遲對(duì)象被解決(resolve)時(shí),“宿主” Deferred(延遲)對(duì)象才會(huì)解決(resolved)該方法,或者當(dāng)其中有一個(gè)延遲對(duì)象被拒絕(rejected)時(shí),“宿主” Deferred(延遲)對(duì)象就會(huì)reject(拒絕)該方法。
    var d1 = $.Deferred();
    var d2 = $.Deferred();
    $.when( d1, d2 ).done(function ( v1, v2 ) {
      console.log( v1 ); // "Fish"
      console.log( v2 ); // "Pizza"
    });
    d1.resolve( "Fish" );
    d2.resolve( "Pizza" );
    (8)deferred.then(doneFilter [,failFilter] [,progressFilter]) -- 當(dāng)Deferred(延遲)對(duì)象解決,拒絕或仍在進(jìn)行中時(shí),調(diào)用添加處理程序。
    參數(shù):
    doneFilter --   type(Function),當(dāng)Deferred(延遲)對(duì)象得到解決時(shí)被調(diào)用的一個(gè)函數(shù)。
    failFilter --   type(Function),當(dāng)Deferred(延遲)對(duì)象拒絕時(shí)被調(diào)用的一個(gè)函數(shù),可選。
    progressFilter --   type(Function),當(dāng)Deferred(延遲)對(duì)象生成進(jìn)度通知時(shí)被調(diào)用的一個(gè)函數(shù),可選。
    其實(shí),then方法可以理解成,把done(),fail(),progress()合在一起寫(xiě)。
    var filterResolve = function () {
       var dfd = $.Deferred(),
         filtered = dfd.then(function (value) { return value * 2; });
       dfd.resolve(5);
       filtered.done(function (value) { console.log(value); });
    };
    filterResolve(); //'10'
    var defer = $.Deferred(),
       filtered = defer.then(null, function (value) {
         return value * 3;
       });
    defer.reject(6);
    filtered.fail(function (value) {
       alert("Value is 3*6 = " + value);
    });
    (9)deferred.always(alwaysCallbacks [,alwaysCallbacks]) -- 當(dāng)Deferred(延遲)對(duì)象解決或拒絕時(shí),執(zhí)行alwaysCallbacks。
     顧名思義,只要Deferred對(duì)象的狀態(tài)發(fā)生更改(解決或者拒絕)均會(huì)調(diào)用alwaysCallbacks。
    (10)deferred.state() -- 獲取一個(gè)Deferred(延遲)對(duì)象的當(dāng)前狀態(tài),不接受任何參數(shù)。
    $.Deferred().state();//"pending"
    上面講述過(guò)deferre(延遲)對(duì)象的三種狀態(tài),這個(gè)方法對(duì)于debug非常有用,例如,在準(zhǔn)備reject一個(gè)deferred對(duì)象之前,判斷它是否處于resolved狀態(tài)。
    (11)deferred.notify(args) and deferred.notifyWith()
    (12)deferred.progress()
    (13)deferred.pipe()
    (14).promise()
    (15)deferred.isRejected() 和 deferred.isResolved() --  從jQuery 1.7開(kāi)始被棄用,較新版本的jQuery類(lèi)庫(kù)中已經(jīng)被刪除,可以使用state()方法代替這兩個(gè)方法。
    (16)deferred.pipe() -- 從jQuery 1.8開(kāi)始被棄用。
    4.什么情況下使用deferred對(duì)象和Promises?
    上面講了很多,那么我們究竟在什么情況下使用Deferred對(duì)象和Promises對(duì)象呢?
    (1)復(fù)雜的動(dòng)畫(huà)
    不知道動(dòng)畫(huà)什么時(shí)候結(jié)束,但是又必須在動(dòng)畫(huà)結(jié)束的時(shí)候做一些操作或者是啟動(dòng)其他的動(dòng)畫(huà),這種情況下,如果采用其他的方式,很容易導(dǎo)致代碼可讀性差,尤其是還夾帶著一些其它的操作,比如渲染、表單操作等,現(xiàn)在jQuery會(huì)為你的動(dòng)畫(huà)操作返回一個(gè)Promise,這樣這些動(dòng)畫(huà)可以進(jìn)行鏈?zhǔn)讲僮鳌?BR>    (2)處理隊(duì)列
    復(fù)制代碼 代碼如下:
    window.queue = $.when() $('#list').on('click', function() { window.queue = window.queue.then(function() { //do the thing }) } )
    (3)The Wait promise
    function wait(ms) { 
      var deferred = $.Deferred(); 
      setTimeout(function(){deferred.resolve()}, ms);
      return deferred.promise(); 
    }
    wait(1500).then(function () {
        // After 1500ms this will be executed 
    });
    (4)典型的Ajax操作
    $.when($.ajax({}), $.ajax({}))
    .done(function(){ alert("success!"); })
    .fail(function(){ alert("error!"); });
    (5)一些耗時(shí)的大循環(huán)操作
    以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。