jQuery的deferred对象解析

deferred对象定义了多种方法,具体说明如下:

  • $.Deferred():生成一个deferred对象。

  • deferred.done():指定操作成功时的回调函数。 

  • deferred.fail():指定操作失败时的回调函数。

  • deferred.promise():没有参数时,返回一个新的deferred对象,该对象的运行状态无法被改变;接收参数时,其用为在参数对象上部署deferred接口。

  • deferred.resolve():手动改变deferred对象的运行状态为“已完成”,从而立即触发done()方法

  • deferred.reject():与deferred.resolve()正好相反,调用后将deferred对象的运行状态变为“已失败”,从而立即触发fail()方法。

  • $.when():为多个操作指定回调函数

$.when($.ajax("/main.php")).then(successFunc,failureFunc);
  • deferred.then():有时为了省事,可以把done()和fail()合在一起写,这就是then()方法。
    如果then()有两个参数,那么第一个参数是done()方法的回调函数,第二个参数是fail()方法的回调函数。如果then()只有一个参数,那么等同于done()。

  • deferred.always():定义回调函数,它的作用是不管调用的是deferred.resolve()还是deferred.reject(),最后总是执行

    1.指定同一操作的多个回调函数

        $.ajax("test.html")
            .done(function () { console.log("哈哈,成功了!"); })
            .fail(function () { console.log("出错啦!"); })        //如果fail,就不会执行后面的:第二个回调函数!
            .done(function(){ console.log("第二个回调函数!");} );

2.为多个操作指定回调函数


deferred对象的另一大好处,就是它允许你为多个事件指定一个回调函数,这是传统写法做不到的。

请看下面的代码,它用到了一个新的方法$.when()

     <script>
        $.when($.ajax("test1.html"), $.ajax("test2.html"))
            .done(function (xhr) {
                console.log(xhr);  //这里的xhr没有返回test1
                 console.log("哈哈,成功了!"); } //都成功了,就运行done()指定的回调函数
                 )

            .fail(function () { console.log("出错啦!"); }); //如果有一个失败或都失败了,就执行fail()指定的回调函数。
    </script>

这段代码的意思是,先执行两个操作$.ajax("test.html")和$.ajax("test1.html"),如果都成功了,就运行done()指定的回调函数;如果有一个失败或都失败了,就执行fail()指定的回调函数。

3.普通操作的回调函数接口(上)

jQuery.when()

jQuery.when( deferreds )
deferreds
类型: Deferred 或 Promise 或 Thenable
零个或多个延迟对象,或者普通的JavaScript对象。

在多个延迟对象传递给jQuery.when() 的情况下,该方法根据一个新的“宿主” Deferred(延迟)对象,跟踪所有已通过Deferreds聚集状态,返回一个Promise对象。

1.当所有的延迟对象被解决(resolve)时,“宿主” Deferred(延迟)对象才会解决(resolved)该方法,

2.或者当其中有一个延迟对象被拒绝(rejected)时,“宿主” Deferred(延迟)对象就会reject(拒绝)该方法。

3.如果“宿主” Deferred(延迟)对象是(resolved)解决状态时, “宿主” Deferred(延迟)对象的 doneCallbacks (解决回调)将被执行。

4.参数传递给 doneCallbacks提供这解决(resolved)值给每个对应的Deferreds对象,并匹配Deferreds传递给 jQuery.when()的顺序

        var d1 = $.Deferred();
        var d2 = $.Deferred();
        var d3 = $.Deferred();

        $.when(d1, d2, d3).done(function (v1, v2, v3) {
            console.log(v1); // v1 is undefined        4.匹配Deferreds传递给 `jQuery.when()`的顺序
            console.log(v2); // v2 is "abc"
            console.log(v3); // v3 is an array [ 1, 2, 3, 4, 5 ]
        }).fail(function (s1, s2, s3) {
            console.log(s1); // s1 is 1                    //2.2.或者当其中有一个延迟对象被拒绝(rejected)时
            console.log(s2); // s2 is 2
            console.log(s3); // s3 is 3
        });

        //d1.reject();                //2.1 或者当其中有一个延迟对象被拒绝(rejected)时
        d1.resolve();                //3.如果“宿主” Deferred(延迟)对象是(resolved)解决状态时
        d2.resolve("abc");
        d3.resolve(1, 2, 3, 4, 5);    //1.当所有的延迟对象被解决(resolve)时

$.When的应用

任何一个操作----不管是ajax操作还是本地操作,也不管是异步操作还是同步操作----都可以使用deferred对象的各种方法,指定回调函数。

我们来看一个具体的例子。假定有一个很耗时的操作wait:

        var wait = function () {

            var tasks = function () {

                console.log("1 现在执行完毕!");

            };

            setTimeout(tasks, 5000);

        };
        $.when(wait())

            .done(function () { console.log("2 哈哈,最后我们成功了!"); })  //立即执行

            .fail(function () { console.log("3 出错啦!"); });

//输出:
2 哈哈,最后我们成功了!
(过5秒)
1 现在执行完毕!

$.when()的参数只能是deferred对象

但是,这样写的话,done()方法会立即执行,起不到回调函数的作用。原因在于 $.when()的参数只能是deferred对象 ,所以必须对wait()进行改写:

       var dtd = $.Deferred(); // 新建一个deferred对象
        var wait = function (dtd) {

            var tasks = function () {

                console.log("1 现在执行完毕!");
                dtd.resolve(); // 改变deferred对象的执行状态
            };

            setTimeout(tasks, 5000);
            return dtd;     //返回这里deferred对象
        };
        $.when(wait(dtd))

            .done(function () { console.log("2 哈哈,最后我们成功了!"); })

            .fail(function () { console.log("3 出错啦!"); });
//输出:
(过5秒)
1 现在执行完毕!
2 哈哈,最后我们成功了!

wait()函数运行完,就会自动运行done()方法指定的回调函数。

4.deferred.resolve()方法和deferred.reject()方法

deferred对象有三种执行状态----未完成,已完成和已失败。如果执行状态是"已完成"(resolved),deferred对象立刻调用done()方法指定的回调函数;如果执行状态是"已失败",调用fail()方法指定的回调函数;如果执行状态是"未完成",则继续等待,或者调用progress()方法指定的回调函数(jQuery1.7版本添加)。

5.deferred.promise()方法

其实就是创建一个当前 Defferred 的只读副本,防止被外部改变。

jQuery提供的deferred.promise()方法的作用是,在原来的Deferred 对象上返回另一个 Deferred 对象,即受限制的 Promise 对象,受限制的 Promise 对象只开放与改变执行状态无关的方法(比如done()方法和fail()方法),屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法),从而使得执行状态不能被改变。

前面的例子dtd是一个全局对象,所以它的执行状态可以从外部改变。

 var wait = function (dtd) {

            var tasks = function () {

                console.log("执行完毕!");
                dtd.resolve(); 
            };

            setTimeout(tasks, 5000);
            return dtd;     
        };
        $.when(wait(dtd))

            .done(function () { console.log("哈哈,成功了!"); })

            .fail(function () { console.log("出错啦!"); });

            dtd.resolve(); // 这里改变状态后,直接就执行done了
    //输出:
哈哈,成功了!
(过5秒)
执行完毕!

为了避免这种情况,jQuery提供了deferred.promise()方法。屏蔽与改变执行状态有关的方法(比如resolve()方法和reject()方法)

       var dtd = $.Deferred(); 
        var wait = function (dtd) {

            var tasks = function () {

                console.log("执行完毕!");
                dtd.resolve(); 
            };

            setTimeout(tasks, 5000);
            return dtd.promise();     //返回这里promise对象
        };


        var d = wait(dtd); 
        $.when(d)

            .done(function () { console.log("哈哈,成功了!"); })

            .fail(function () { console.log("出错啦!"); });

            d.resolve(); // //d 是一个 promise对象,没有resolve方法. 此时,这个语句是无效的

更好的方法是

        var wait = function (dtd) {
            var dtd = $.Deferred();  //在函数内部,新建一个Deferred对象
            var tasks = function () {

                console.log("执行完毕!");
                dtd.resolve(); 
            };

            setTimeout(tasks, 5000);
            return dtd.promise();     //返回这里promise对象
        };

        $.when(wait())

            .done(function () { console.log("哈哈,成功了!"); })

            .fail(function () { console.log("出错啦!"); });

6.另外一种写法,deferred对象的建构函数$.Deferred()(一般用第1种)

另一种防止执行状态被外部改变的方法,是使用deferred对象的建构函数$.Deferred()。

这时,wait函数还是保持不变,我们直接把它传入$.Deferred():

$.Deferred(wait)

  .done(function(){ alert("哈哈,成功了!"); })

  .fail(function(){ alert("出错啦!"); });

jQuery规定,$.Deferred()可以接受一个函数名(注意,是函数名)作为参数,$.Deferred()所生成的deferred对象将作为这个函数的默认参数。

7.普通操作的回调函数接口(下):第3种写法(一般用第1种)

除了上面两种方法以外,我们还可以直接在wait对象上部署deferred接口。

  var dtd = $.Deferred(); 

  var wait = function(dtd){

    var tasks = function(){

      alert("执行完毕!");

      dtd.resolve(); 

    };

    setTimeout(tasks,5000);

  };

  dtd.promise(wait); //重点,wait对象上部署Deferred接口,直接调用done和fail

  wait.done(function(){ alert("哈哈,成功了!"); })

  .fail(function(){ alert("出错啦!"); });

  wait(dtd);

这里的关键是 dtd.promise(wait) 这一行,它的作用就是在wait对象上部署Deferred接口 。正是因为有了这一行,后面才能直接在wait上面调用done()和fail()。

8.deferred.then() 快捷写法

有时为了省事,可以把done()和fail()合在一起写,这就是then()方法。

    $.when($.ajax( "/main.php" ))

    .then(successFunc, failureFunc );

如果then()有两个参数,那么第一个参数是done()方法的回调函数,第二个参数是fail()方法的回调方法。如果then()只有一个参数,那么等同于done()。

更新一下

then方法的作用也是指定回调函数,它可以接受三个参数,也就是三个回调函数。第一个参数是resolve时调用的回调函数(相当于done方法),第二个参数是reject时调用的回调函数(相当于fail方法),第三个参数是progress()方法调用的回调函数。

返回值


在jQuery 1.8之前,then()只是.done().fail()写法的语法糖,两种写法是等价的。在jQuery 1.8之后,then()返回一个新的promise对象,而done()返回的是原有的deferred对象。如果then()指定的回调函数有返回值,该返回值会作为参数,传入后面的回调函数。

9. Durandal 实例

self.loadContryList = function (num) {
                       //system.defer 可以替换成 jquery 的 $.Deferred
                return system.defer(function (dfd) {
                    membershipVAT.getAllCountryList().done(function (xhr) {
                        setTimeout(function () { console.log(num); }, 3000);
                        return dfd.resolve(true);
                    }).fail(function (xhr) {
                        return dfd.resolve(false);
                    })
                }).promise();
            }

 var der = self.loadContryList(1);
                der.then(function (xhr) {
                        console.log('aaaaaaaaaaaaaaaa');
                       return self.loadContryList(2);
                    }).then(function (data) {
                        return self.loadContryList(3);
                    }).then(function (data) {
                        return  self.loadContryList(4);
                    });

//----------------------------单个控制
                self.getProjectTaskLinguisticQA = function () {

                //var tasks = function () {
                return system.defer(function (dtd) {
                    console.log("执行完毕1!");
                    dtd.reject(1);
                    //};

                    //setTimeout(tasks, 1000);
                }).promise();
            }

            self.getProjectTaskLinguisticQA2 = function () {
                //var tasks = function () {
                return system.defer(function (dtd) {
                    dtd.reject(2);
                    console.log("执行完毕2!");
                    //};

                    //setTimeout(tasks, 4000);
                }).promise();
            }

            self.getProjectTaskLinguisticQA3 = function () {
                //var tasks = function () {
                return system.defer(function (dtd) {
                    dtd.resolve(3);
                    //dtd.reject(4);
                    console.log("执行完毕3!");

                    //};

                    //setTimeout(tasks, 4000);
                }).promise();
            }
                self.getProjectTaskLinguisticQA3()
                    .done(function (a, b) {
                        console.log("成功了g3")
                        self.getProjectTaskLinguisticQA()
                            .done(function (a, b) {
                                console.log("成功了g1")
                                self.getProjectTaskLinguisticQA2()
                                    .done(function (i, j) {
                                        console.log("成功了g2")
                                    })
                                    .fail(function (c, d) {
                                        console.log("失败了g2")
                                    });
                            })
                            .fail(function (c, d) {
                                console.log("失败了g1")
                            })
                    })
                    .fail(function (c, d) {
                        console.log("失败了g3")
                    })

链式调用

then

function asyncRequest(number) {  
    // 假设这是一个返回 Promise 的异步操作,比如 AJAX 请求  
    return new Promise((resolve, reject) => {  
        setTimeout(() => {  
            if (Math.random() < 0.5) {  
                // 模拟成功的情况  
                console.log('异步请求 ' + number + ' 成功');  
                resolve(number);  
            } else {  
                // 模拟失败的情况  
                console.log('异步请求 ' + number + ' 失败');  
                reject(number);  
            }  
        }, number * 1000); // 假设每个请求需要不同的时间来完成  
    });  
}  
  
// 开始执行第一个异步请求,并链式调用后续请求  
asyncRequest(1)  
    .then((result1) => {  
        console.log('第一个请求执行完成');  
        return asyncRequest(2); // 返回第二个请求的 Promise  
    })  
    .then((result2) => {  
        console.log('第二个请求执行完成');  
        // 同时执行第三和第四个请求,使用 Promise.all 等待它们完成  
        return Promise.all([  
            asyncRequest(3),  
            asyncRequest(4)  
        ]);  
    })  
    .then(([result3, result4]) => {  
        console.log('第三和第四个请求执行完成');  
        return asyncRequest(5); // 返回第五个请求的 Promise  
    })  
    .then((result5) => {  
        console.log('所有请求执行完毕,最终结果是:', result5);  
    })  
    .catch((error) => {  
        // 捕获任何地方的错误  
        console.log('某个请求失败:', error);  
    });

done和fail

而且不用then

function asyncRequest(number) {  
    var defer = $.Deferred();  
      
    // 假设这是一个异步操作,比如 AJAX 请求  
    setTimeout(function() {  
        if (Math.random() < 0.5) {  
            // 模拟成功的情况  
            console.log('异步请求 ' + number + ' 成功');  
            defer.resolve(number);  
        } else {  
            // 模拟失败的情况  
            console.log('异步请求 ' + number + ' 失败');  
            defer.reject(number);  
        }  
    }, number * 1000); // 假设每个请求需要不同的时间来完成  
      
    return defer.promise();  
}  
  
// 开始执行第一个异步请求,并链式调用后续请求  
asyncRequest(1).done(function(result1) {  
    console.log('第一个请求执行完成');  
    return asyncRequest(2); // 返回第二个请求的 promise  
}).done(function(result2) {  
    console.log('第二个请求执行完成');  
    // 同时执行第三和第四个请求,使用 $.when 等待它们完成  
    return $.when(  
        asyncRequest(3),  
        asyncRequest(4)  
    );  
}).done(function(result3, result4) {  
    console.log('第三和第四个请求执行完成');  
    // 由于 $.when 返回的是一个包含所有结果的数组,  
    // 所以我们需要使用 result3 和 result4 来分别获取它们  
    return asyncRequest(5); // 返回第五个请求的 promise  
}).done(function(result5) {  
    console.log('所有请求执行完毕,最终结果是:', result5);  
}).fail(function(error) {  
    console.log('某个请求失败:', error);  
});

在这个例子中,每个 asyncRequest 函数返回一个 promise 对象,它可以在成功时通过 .done() 方法链式调用下一个请求,或者在失败时通过 .fail() 方法处理错误。当需要同时执行多个请求时(如第三和第四个请求),我们使用 $.when() 来等待它们完成。

请注意,.when() 方法返回一个新的 promise 对象,这个对象在所有给定的 promises 都完成时解析,并且其解析值是一个包含所有原 promises 解析值的数组。因此,在 .done() 的回调函数中,我们使用 result3 和 result4 来分别获取第三和第四个请求的结果。

这样,您就可以通过链式调用 .done() 方法来顺序执行异步请求,并在每个请求成功时启动下一个请求。如果在任何时候发生错误,.fail() 方法将被调用,并且错误会被传递给相应的回调函数。

参考:

jQuery的deferred对象详解:http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html

posted @ 2019-04-02 23:00  【唐】三三  阅读(249)  评论(0编辑  收藏  举报