原博文地址:http://www.cnblogs.com/snandy/archive/2012/12/19/2812935.html

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标示符$上挂了两个方法,如下

 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对象的核心方法

  • done/fail/progress 是 callbacks.add,将回调函数存入
  • resolve/reject/notify 是 callbacks.fire,执行回调函数(或队列)

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

一、done/resolve

 function cb() {
alert('success')
}
var deferred = $.Deferred()
deferred.done(cb)
setTimeout(function() {
deferred.resolve()
}, 3000)

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

二、fail/reject

 function cb() {
alert('fail')
}
var deferred = $.Deferred()
deferred.fail(cb)
setTimeout(function() {
deferred.reject()
}, 3000)

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

三、progress/notify

 function cb() {
alert('progress')
}
var deferred = $.Deferred()
deferred.progress(cb)
setInterval(function() {
deferred.notify()
}, 2000)

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

四、链式操作

 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,一次添加成功,失败,进度回调函数

 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方法为成功,失败状态添加同一个回调函数

 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方法保证多个异步操作全部成功后才回调

 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的风格可以这样了

 $.ajax(url)
.done(success)
.fail(fail)

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

 $.ajax(url)
.done(success1)
.done(success2)
.fail(fail2)
.fail(fail2)

1.5之前的则不行

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

 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

原博文地址:http://www.cnblogs.com/snandy/archive/2012/12/19/2812935.html

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

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

    Deferred对象是由$.Deferred构造的,$.Deferred被实现为简单工厂模式. 它用来解决JS中的异步编程,它遵循 Common Promise/A 规范.实现此规范的还有 when. ...

  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. Yii ExtendedActiveRecord 增强版 ActiveRecord 增加多数据库连接绑定功能

    ExtendedActiveRecord 继承自 CActiveRecord,因此基础功能与 CActiveRecord 无异 为添加对多数据库连接的支持,增加了对 connectionName() ...

  2. 【HTML5】使用 JavaScript 来获取电池状态(Battery Status API)

    HTML5 规范已经越来越成熟,可以让你访问更多来自设备的信息,其中包括最近提交的 "Battery Status API".如其名称所示,该 API 允许你通过 JavaScri ...

  3. 使用LeakCanary进行内存泄漏追踪

    LeakCanary使用 1.在build.gradle 中 dependencies {     //添加     debugCompile 'com.squareup.leakcanary:lea ...

  4. New Concept English there (5)

    25w/m Editors of newspapers and magazines often go to extremes to provide their readers with unimpor ...

  5. Linux系统在启动过程中grub引导文件丢失的解决方法

    在/boot/grub2目录下有一个grub.cfg文件:该文件主要是用来自动地引导系统启动内核程序和系统的初始化程序. 问题一:当系统在启动的情况下,我们不小心删除/boot/grub2/grub. ...

  6. DHL学习--<asp:literal

    <asp:literal  ID="ltlJS" runat="server"></asp:literal> 标签的Text属性可以放J ...

  7. jsp采用数据库连接池的方法获取数据库时间戳context.xml配置,jsp页面把时间格式化成自己需要的格式

    <?xml version="1.0" encoding="UTF-8"?> <!-- 数据库连接池配置文件 --> <Conte ...

  8. ss-libev 源码解析udp篇 (4)

    本篇分析remote_recv_cb,这是整个udp转发的反方向,即读取从后端发送过来的数据再发送给前端.对于ss-server,读取到的数据是目标地址的udp服务器发送回来的响应数据,ss-serv ...

  9. angular2.0学习日记1

    使用NG2之前需要安装node以及Npm环境,并到node下下载ng2所需要得文件,具体配置请到https://angular.cn/docs/ts/latest/quickstart.html按照提 ...

  10. HTML中Div、span、label标签的区别

    div与span 大家在初学div+css布局时,有很多困惑,在div与span的使用过程没觉得有一定的”章法”,觉得两个区别不大,在w3c的关于div和span的定义:div作为分割文档结构自然使它 ...