Home > Web Front-end > JS Tutorial > jquery Deferred对象讲解

jquery Deferred对象讲解

WBOY
Release: 2016-06-01 09:54:11
Original
862 people have browsed it

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

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

 

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

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

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

 

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

<code class="language-javascript">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();
    }
});</code>
Copy after login

 

$.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和.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

<code class="language-javascript">function cb() {
    alert('success')
}
var deferred = $.Deferred()
deferred.done(cb)
setTimeout(function() {
    deferred.resolve()
}, 3000)</code>
Copy after login

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

 

二、fail/reject

<code class="language-javascript">function cb() {
    alert('fail')
}
var deferred = $.Deferred()
deferred.fail(cb)
setTimeout(function() {
    deferred.reject()
}, 3000)</code>
Copy after login

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

 

三、progress/notify

<code class="language-javascript">function cb() {
    alert('progress')
}
var deferred = $.Deferred()
deferred.progress(cb)
setInterval(function() {
    deferred.notify()
}, 2000)</code>
Copy after login

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

 

四、链式操作

<code class="language-javascript">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)</code>
Copy after login

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

 

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

<code class="language-javascript">function fn1() {
    alert('success')
}
function fn2() {
    alert('fail')
}
function fn3() {
    alert('progress')
}
var deferred = $.Deferred()
deferred.then(fn1, fn2, fn3)</code>
Copy after login

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

 

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

<code class="language-javascript">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)</code>
Copy after login

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

 

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

<code class="language-javascript">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)</code>
Copy after login

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

 

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

 

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

<code class="language-javascript">$.ajax(url)
 .done(success)
 .fail(fail)</code>
Copy after login

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

<code class="language-javascript">$.ajax(url)
 .done(success1)
 .done(success2)
 .fail(fail2)
 .fail(fail2)</code>
Copy after login

1.5之前的则不行

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

<code class="language-javascript">var ajax1 = $.ajax(url1)
var ajax2 = $.ajax(url2)
$.when(ajax1, ajax2).done(success)</code>
Copy after login

如果项目中有一些异步问题不妨用用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

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template