js deferred

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


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

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

下面举一些示例看看如何使用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

时间: 09-24

js deferred的相关文章

JQuery 加载 CSS、JS 文件的方法有哪些?

在进行web前端开发(http://www.maiziedu.com/course/web-px/)时,我们可能会需要使用JQuery加载一个外部的css文件或者js文件,加载外部文件的方法有多种,下面具体看看各种加载方法 JS 方式加载 CSS.JS 文件: //加载 css 文件function includeCss(filename) { var head = document.getElementsByTagName('head')[0]; var link = document.cre

使用grunt合并压缩js、css文件

需要了解的知识: 1.nodejs的安装与命令行使用 2.nodejs安装应用 3.grunt的初步了解 本文已假定读者已经熟悉以上知识. 好,我们继续: 任务1:将src目录下的所有zepto及插件合并,并压缩. --src/ ajax.js assets.js callbacks.js data.js deferred.js detect.js event.js form.js fx.js fx_methods.js gesture.js ie.js ios3.js selector.js

ionic路由传值

ionic路由传值 app.js ===================================== //新建工作任务.state(‘app.newTask’, angularAMD.route({url: ‘/newTask?:userid:username:img:isclear:arr1:arr2:arr3′,//cache:’false’,views: {‘menuContent’: {templateUrl: ‘app/templates/oa/NewTask.html’,co

针对移动端的前端工作流

在移动端项目的开发中,我们会遇到诸如移动端技术选型.移动端适配.预处理器语言的使用规范和编译.各种繁琐的工作(压缩.合并.内联.雪碧图.CSS前缀--)等问题,在一遍又一遍的重复劳动中,我们需要一个能够结合最佳实践的项目初始文件,本套工作流就是以这个目的进行构建,旨在提高移动端项目开发效率. 这套工作流与其叫工作流这么高大上的名字,我更愿意叫它基础库.如它的名字一样,它就是用来作为项目的基础,在良好的基础上开发的项目,才像是站在巨人的肩膀上,才能保证项目的质量与效率. 此外因为是针对移动端(尤其

高效编写微信小程序

原文:https://isux.tencent.com/high-performance-wechat-app-development.html 前言 微信小程序是一个工程,就和盖房子一样,打好了地基,才能保证后续工程师建立在可靠牢固的基础上. 笔者需要经常新建项目,每次都要重复"修改项目结构 -> 从老项目中复制粘贴文件 -> 删除一些老项目中代码"这样的过程,实在是-费心费力. 另一个痛点是:每次新建小程序页面要生成三个文件名相同的文件 ( .wxml..wxss 和

AngularJS 中的 Promise 和 设计模式(转)

原文地址:http://my.oschina.net/ilivebox/blog/293771 目录[-] Promise 简单例子 链式 Promise Parallel Promises And 'Promise-Ifying' Plain Values AngularJS 中的实际应用 实际链式例子 测试 Promise - 基于代码 Promises And Design Patterns 写得好长好长好长长~ 解决 Javascript 异步事件的传统方式是回调函数:调用一个方法,然后

Deferred解决JS同步问题

测试脚本: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"> <

JS魔法堂:jQuery.Deferred(jQuery1.5-2.1)源码剖析

一.前言 jQuery.Deferred作为1.5的新特性出现在jQuery上,而jQuery.ajax函数也做了相应的调整.因此我们能如下的使用xhr请求调用,并实现事件处理函数晚绑定. var promise = $.getJSON('dummy.js') // 其他逻辑处理 promise.then(function(){ alert('late binding') }) 我还一度以为这就是Promises/A+规范的实现,但其实jQuery.Deferred应该与jsDeferred归为

Deferred跟promise跟js同步问题记录

之前的时候,碰到过几次同事问我,说js的同步怎么处理,就是我想先执行这段代码(耗时相对较长的一行,多数是异步的一些api调用),执行完了之后我再执行下边这句,每次我都很无奈的说不晓得,如果是ajax的话我知道async可以搞定,当然这并没有什么乱用.近段时间在做一个自动化测试的项目,前后端分离的.旁边的同事要做前端websql的增删改查,一直在说jquery的deferred如何如何,我也不懂,就感觉听上去高大上的一个东西,内心忐忑之余就找两篇文章读了一下,然后就有了此文. ----------