Deferred对象是由$.Deferred构造的,$.Deferred被实现为简单工厂模式

它用来解决JS中的异步编程,它遵循 Common Promise/A 规范。实现此规范的还有 when.js 和 dojo

$.Deferred作为新特性首次出现在版本1.5中,这个版本利用Deferred又完全重写了Ajax模块。

$.Deferred在jQuery代码自身四处被使用,分别是promise方法、DOM readyAjax模块、动画模块。

这里以版本1.8.3分析,由于1.7$.Callbacks从Deferred中抽离出去了,目前版本的deferred.js代码不过150行,而真正$.Deferred的实现只有100行左右。

$.extend标示符$上挂了两个方法,如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
jQuery.extend({
    Deferred: function( func ) {
        var tuples = [
                // action, add listener, listener list, final state
                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
                [ "notify", "progress", jQuery.Callbacks("memory") ]
            ],
        ...
 
        // All done!
        return deferred;
    },
    // Deferred helper
    when: function( subordinate /* , ..., subordinateN */ ) {
        var i = 0,
            resolveValues = core_slice.call( arguments ),
            length = resolveValues.length,
            ....
 
        return deferred.promise();
    }
});

$.Deferred的实现

  1. 创建三个$.Callbacks对象,分别表示成功,失败,处理中三种状态
  2. 创建了一个promise对象,具有state、always、then、primise方法
  3. 通过扩展primise对象生成最终的Deferred对象,返回该对象

$.when的实现

  1. 接受若干个对象,参数仅一个且非Deferred对象将立即执行回调函数
  2. Deferred对象和非Deferred对象混杂时,对于非Deferred对象remaining减1
  3. Deferred对象总数 = 内部构建的Deferred对象 + 所传参数中包含的Deferred对象
  4. 所传参数中所有Deferred对象每当resolve时remaining减1,直到为0时(所有都resolve)执行回调

这就是$.Deferred和$.when的全部了,各个方法及使用稍后介绍。

代码阅读中会发现then和when方法的实现最难理解,看多次,后感回味无穷,非常巧妙。then内部会用到不同寻常的递归,when用到了计数,每次异步成功后减一,直到为0后表示全部异步操作成功,这时才可执行回调。

上面提到Deferred里有3个$.Callbacks的实例,Deferred自身则围绕这三个对象进行更高层次的抽象。以下是Deferred对象的核心方法

下面举一些示例看看如何使用Deferred对象。

一、done/resolve

1
2
3
4
5
6
7
8
function cb() {
    alert('success')
}
var deferred = $.Deferred()
deferred.done(cb)
setTimeout(function() {
    deferred.resolve()
}, 3000)

在HTTP中表示后台返回成功状态(如200)时使用,即请求成功后可执行成功回调函数。

二、fail/reject

1
2
3
4
5
6
7
8
function cb() {
    alert('fail')
}
var deferred = $.Deferred()
deferred.fail(cb)
setTimeout(function() {
    deferred.reject()
}, 3000)

在HTTP中表示后台返回非成功状态时使用,即请求失败后可执行失败回调函数。

三、progress/notify

1
2
3
4
5
6
7
8
function cb() {
    alert('progress')
}
var deferred = $.Deferred()
deferred.progress(cb)
setInterval(function() {
    deferred.notify()
}, 2000)

在HTTP中表示请求过程中使用,即请求过程中不断执行回调函数。这可用在文件上传时的loading百分比或进度条。

四、链式操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function fn1() {
    alert('success')
}
function fn2() {
    alert('fail')
}
function fn3() {
    alert('progress')
}
var deferred = $.Deferred()
deferred.done(fn1).fail(fn2).progress(fn3) // 链式操作
setTimeout(function() {
    deferred.resolve()
    //deferred.reject()
    //deferred.notify()
}, 3000)

这样可以很方便了添加成功,失败,进度回调函数。

五,便利函数then,一次添加成功,失败,进度回调函数

1
2
3
4
5
6
7
8
9
10
11
function fn1() {
    alert('success')
}
function fn2() {
    alert('fail')
}
function fn3() {
    alert('progress')
}
var deferred = $.Deferred()
deferred.then(fn1, fn2, fn3)

调用then后还可以继续链式调用then添加多个不同回调函数,这个then也正是jQuery对 Common Promise/A 的实现。

六、使用always方法为成功,失败状态添加同一个回调函数

1
2
3
4
5
6
7
8
9
10
11
12
13
var deferred = $.Deferred()
deferred.always(function() {
    var state = deferred.state()
    if ( state === 'resolved') {
        alert('success')
    } else if (state === 'rejected') {
        alert('fail')
    }
})
setTimeout(function() {
    deferred.resolve()
    //deferred.reject()
}, 3000)

回调函数中可以使用deferred.state方法获取异步过程中的最终状态,这里我调用的是deferred.resolve,因此最后的状态是resolved,表示成功。

七、when方法保证多个异步操作全部成功后才回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function fn1() {
    alert('done1')
}
function fn2() {
    alert('done2')
}
function fn3() {
    alert('all done')
}
 
var deferred1 = $.Deferred()
var deferred2 = $.Deferred()
 
deferred1.done(fn1)
deferred2.done(fn2)
$.when(deferred1, deferred2).done(fn3)
 
setTimeout(function() {
    deferred1.resolve()
    deferred2.resolve()
}, 3000)

先后弹出了done1、done2、all done。 如果setTimeout中有一个reject了,fn3将不会被执行。

八、deferred.promise()方法返回只能添加回调的对象,这个对象与$.Deferred()返回的对象不同,只能 done/fail/progress,不能resolve/reject/notify。即只能调用callbacks.add,没有 callbacks.fire。它是正统Deferred对象的阉割版。

有了Deferred,我们使用jQuery书写ajax的风格可以这样了

1
2
3
$.ajax(url)
 .done(success)
 .fail(fail)

看似和以前比较也没什么优点,但它还可以添加多个回调

1
2
3
4
5
$.ajax(url)
 .done(success1)
 .done(success2)
 .fail(fail2)
 .fail(fail2)

1.5之前的则不行

如果多个请求完成后才算成功,1.5之前的是无法解决的,现在则可以用$.when搞定

1
2
3
var ajax1 = $.ajax(url1)
var ajax2 = $.ajax(url2)
$.when(ajax1, ajax2).done(success)

如果项目中有一些异步问题不妨用用Derferred。

相关:

http://jimliu.net/?p=64

http://www.infoq.com/cn/news/2011/09/js-promise

http://www.erichynds.com/jquery/using-deferreds-in-jquery/

http://sitr.us/2012/07/31/promise-pipelines-in-javascript.html

http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html

读jQuery之二十(Deferred对象)的更多相关文章

  1. 读jQuery之二十(Deferred对象)--(转)

    原博文地址:http://www.cnblogs.com/snandy/archive/2012/12/19/2812935.html Deferred对象是由.Deferred构造的,.Deferr ...

  2. deferred对象和promise对象(二)---deferred对象

    早上醒来,继续讨论deferred对象和primise对象. deferred对象的的方法: 1.$.Deferred()-----生成一个deferred对象 2.deferred.done()-- ...

  3. jQuery入门二(DOM对象与jQuery对象互相转换)

    - DOM对象与jQuery对象互相转换 第一篇说过,DOM对象不能调用jQuery对象的属性和方法,同样jQuery对象也不能调用DOM对象的属性和方法.但是在实际开发中,可能两者间需要互相调用对方 ...

  4. jQuery的deferred对象详解(二)

    Deferred对象是由$.Deferred构造的,$.Deferred被实现为简单的工厂模式. $.Deferred的实现 创建三个$.Callbacks对象,分别表示成功done,失败fail,处 ...

  5. 亲自打造Deferred对象

    经过对比之后,决心学习jQuery,自己打造一个Deferred对象.var util = require('./util.js');function Callbacks() { var list = ...

  6. jQuery中deferred对象的使用(二)

    接上一回的内容,漏了一个always()方法,参数也是回调函数,与done和fail不同的是,无论任何情况都执行always方法中的回调. deferred对象的使用(二) deferred对象不光可 ...

  7. 读jQuery源码 - Deferred

    Deferred首次出现在jQuery 1.5中,在jQuery 1.8之后被改写,它的出现抹平了javascript中的大量回调产生的金字塔,提供了异步编程的能力,它主要服役于jQuery.ajax ...

  8. 【转载】学习资料存档:jQuery的deferred对象详解

    我在以前的文章里提到promise和deferred,这两个东西其实是对回调函数的一种写法,javascript的难点之一是回调函数,但是我们要写出优秀的javascript代码又不得不灵活运用回调函 ...

  9. jQuery的deferred对象详解(一)

    最近一段时间,都在研究jquery里面的$.Deffered对象,几天都搞不明白,其中源码的运行机制,网上查找了相关的资料,<jQuery的deferred对象详解>阮一峰老师的文章,里面 ...

随机推荐

  1. 集合-字典(Lookup/SortedDictionary)

    Lookup<TKey, TElement>非常类似于Dictionary<TKey, TValue>,但是把键映射在一个值集上. 必须调用ToLookup方法创建Lookup ...

  2. OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波

    http://blog.csdn.net/chenyusiyuan/article/details/8710462 OpenCV学习笔记(27)KAZE 算法原理与源码分析(一)非线性扩散滤波 201 ...

  3. springMVC入门配置及helloworld实例

    1. 新建web project 2. 往lib里copy必须的jar 3. 改写web.xml <?xml version="1.0" encoding="UTF ...

  4. Gamma原理及快速实现算法(C/C++)(转)

    源:Gamma原理及快速实现算法(C/C++) 原文:http://blog.csdn.net/lxy201700/article/details/24929013 参考 http://www.cam ...

  5. unity3d热更新解决方案,使用ulua插件开发的框架。

    ulua插件下载地址 www.ulua.org,下面要说的是ulua的开发框架. 首先是 LuaLoader 类,它负责把一个 lua 的 table 加载起来,使此 lua 的 table 像一个 ...

  6. 【POJ2104】【HDU2665】K-th Number 主席树

    [POJ2104][HDU2665]K-th Number Description You are working for Macrohard company in data structures d ...

  7. IT技术网站汇总

    首先是比较著名的博客型的网站!一般来说在国外比较著名的博客基本上都是比较有影响力发起的或者建立的经常发布一些比较有思考力深入分析的文章! 博客媒体网站 1.www.ArsTechnica.com 2. ...

  8. (中等) HDU 1828 Picture,扫描线。

    Problem Description A number of rectangular posters, photographs and other pictures of the same shap ...

  9. javascript中最常用的方法

    平时在工作中时常需要一些方法,下面列举几个最常用的几个方法. 1. indexOf(searchvalue,fromindex) 该方法用于查找一个字符串是否包含了另一个字符串 indexOf() 方 ...

  10. S3C2440硬件连接解析

    S3c2440是三星公司推出的一款基于ARM920T的处理器,采用ARM内核,不同于单片机,无片上rom与ram,必须搭配相应的外围电路进行使用,现在,让我们从零开始进行这一块MCU的学习,为了入门简 ...