jQuery 2.0.3 源码分析 Deferrred概念

转载http://www.cnblogs.com/aaronjs/p/3348569.html

JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作。这也 是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码逻辑,但是大量异步操作所带来的回调函数,会把我们的算法分解地支离破碎。此时我们不能 用if来实现逻辑分支,也不能用while/for/do来实现循环,更不用提异步操作之间的组合、错误处理以及取消操作了。因此也诞生了如jQuery Deferred这样的辅助类库

举一个例子会有助于理解

我做的是phonegap项目,所以涉及到的异步处理就别特多:

1. 常见的setTimeout

2. 与底层代码通信,调用插件cordova.exec

3. postmessage与iframe通信

4. CSS3 动画

5. Ajax

6. HTML5的本地数据

等等…

我就拿HTML5 Web Database为例:

数据库

查询语句

//数据库链接对象
        config.db = window.openDatabase(config.dbName, "1.0", "Xxtebook Database", config.dbSize);

        config.db.transaction(function(tx) {
                tx.executeSql(

‘SELECT * FROM Novel‘

, [], function(tx, results) {
                    result = results.rows;
                    console.log(‘result‘,result) //
                });
        }, function(){}, function(){});

就这段代码其实本身是没问题的,但是这里至少要涉及到异步方面的考虑:

javaScript要求在与服务器进行交互时要用异步通信,如同AJAX一样,因为是异步模型,所以在调用transaction游览器提供的本 地数据接口时候类似AJAX(这里我是假设),浏览器自己有内部的XHR方法异步处理,但是此时的JS代码还是会同步往下执行,其实就是无阻塞的代码

问题:因为无无阻塞,代码在发送AJAX这个请求后会继续执行,那么后续的操作如果依赖这个数据就会出错了,所以这里就需要等待AJAX返回,才能执行后续操作

我们换个更简单 伪代码

alert(1)
setTimeout(function(){
    alert(2)
},0)
alert(3)
//alert(1)
//alert(3)
//alert(2)

代码都能看懂,因为异步而导致流程不正确

或者说我们的应用在某个程度上依赖第三方api的数据,那么就会面临一个共同的问题:

我们无法获悉一个API响应的延迟时间,应用程序的其他部分可能会被阻塞,直到它返回 结果。Deferreds 对这个问题提供了一个更好的解决方案,它是非阻塞的,并且与代码完全解耦 。

当然异步操作也可以提供一个类似成功回调,失败回调的通知接口

JS是单线程语言,就简单性而言,把每一件事情(包括GUI事件和渲染)都放在一个线程里来处理是一个很好的程序模型,因为这样就无需再考虑线程同步这些复杂问题。
另一方面,他也暴露了应用开发中的一个严重问题,单线程环境看起来对用户请求响应迅速,但是当线程忙于处理其它事情时,就不能对用户的鼠标点击和键盘操作做出响应。


What‘s The Point Of Promises?

http://www.kendoui.com/blogs/teamblog/posts/13-03-28/what-is-the-point-of-promises.aspx

http://wiki.commonjs.org/wiki/Promises/A

大多情况下,promise作为一个模型,提供了一个在软件工程中描述延时(或将来)概念的解决方案。

它背后的思想我们已经介绍过:不是执行一个方法然后阻塞应用程序等待结果返回,而是返回一个promise对象来满足未来值。

这样看来,Promise/A只是一种规范,Deferred可以看作这种规范的具体实现

画了一个很简短的图,大家略过一下,后面有很详细的流程图

我们先看看规范如何定义的:

Promise/A提议’定义了一个’then‘方法来注册回调,当处理函数返回结果时回调会执行。它返回一个promise的伪代码看起来是这样的:

promise = function create(
    //数据库链接对象
    config.db = window.openDatabase(config.dbName, "1.0", "Xxtebook Database", config.dbSize);

    config.db.transaction(function(tx) {
            tx.executeSql(‘SELECT * FROM Novel‘, [], function(tx, results) {
                result = results.rows;
                console.log(‘result‘,result) //
            });
    }, function(){}, function(){});
);

promise.then(function( futureValue ) {

});
promise.then(function( futureValue ) {

});

promise回调会在处于以下两种不同的状态下执行:

  • resolved:在这种情况下,数据是可用
  • rejected:在这种情况下,出现了错误,没有可用的值

‘then‘方法接受两个参数:一个用于promise得到了解决(resolved),另一个用于promise拒绝(rejected)。让我们回到伪代码

promise.then( function( futureValue ) {
   //成功
} , function() {
  //失败
} );

在某些情况下,我们需要获得多个返回结果后,再继续执行应用程序(例如,在用户可以选择他们感兴趣的选项前,显示一组动态的选项)。这种情况下,‘when‘方法可以用来解决所有的promise都满足后才能继续执行的场景。

when(
  promise1,
  promise2,
   ...
 ).then(function( futureValue1, futureValue2, ... ) {
     //当所有的异步对象都准备完毕后,才执行
 });

我们在模拟一个场景

如果同时执行多个动画的时候,我们就需要对每一个动画执行完毕后的处理下一个动作,意味着我们就要监听所有动画的执行完毕后的回调

使用promise和‘when’方式却可以很直截了当的表示: 一旦动画执行完成,就可以执行下一步任务。最终的结果是我们可以可以简单的用一个回调来解决多个动画执行结果的等待问题

when( function(){
   //执行动画1
   return promise
}, function(){
  //执行动画2
  return promise 2
} ).then(function(){
   //当2个动画完成后,我们在自行后续的代码
});

基本上可以用非阻塞的逻辑方式编写代码并异步执行。 而不是直接将回调传递给函数,这可能会导致紧耦合的接口,通过promise模式可以很容易区分同步和异步的概念。

根据这个规范,我们看看JQ是如何实现的?


Deferred Object :http://api.jquery.com/category/deferred-object/

由于1.7版本后回调函数Callbacks从Deferred中抽离出去了,所以在此之前还需要了解下 回调函数-callback

本文主要是涉及源码的实现

我们先对API有个整体的认识

jQuery.Deferred()

一个构造函数,返回一个链式实用对象方法来注册多个回调,回调队列, 调用回调队列,并转达任何同步或异步函数的成功或失败状态。

deferred.always()

当Deferred(延迟)对象解决或拒绝时,调用添加处理程序

deferred.done()

当Deferred(延迟)对象解决时,调用添加处理程序

deferred.fail()

当Deferred(延迟)对象拒绝时,调用添加处理程序。

deferred.isRejected()

确定一个Deferred(延迟)对象是否已被拒绝。

deferred.isResolved()

确定一个Deferred(延迟)对象是否已被解决。

deferred.notify()

根据给定的 args参数 调用Deferred(延迟)对象上进行中的回调 (progressCallbacks)

deferred.notifyWith()

根据给定的上下文(context)和args递延调用Deferred(延迟)对象上进行中的回调(progressCallbacks )

deferred.pipe()

实用的方法来过滤 and/or 链Deferreds。

deferred.progress()

当Deferred(延迟)对象生成进度通知时,调用添加处理程

deferred.promise()

返回Deferred(延迟)的Promise(承诺)对象。

deferred.reject()

拒绝Deferred(延迟)对象,并根据给定的args参数调用任何失败回调函数(failCallbacks)

deferred.rejectWith()

拒绝Deferred(延迟)对象,并根据给定的 context和args参数调用任何失败回调函数(failCallbacks)。

deferred.resolve()

解决Deferred(延迟)对象,并根据给定的args参数调用任何完成回调函数(doneCallbacks)

deferred.resolveWith()

解决Deferred(延迟)对象,并根据给定的 context和args参数调用任何完成回调函数(doneCallbacks)

deferred.state()

确定一个Deferred(延迟)对象的当前状态

deferred.then()

当Deferred(延迟)对象解决,拒绝或仍在进行中时,调用添加处理程序。

jQuery.when()

提供一种方法来执行一个或多个对象的回调函数, Deferred(延迟)对象通常表示异步事件。

.promise()

返回一个 Promise 对象用来观察当某种类型的所有行动绑定到集合,排队与否还是已经完成

构建一个异步对象:

我们传统的写法

function aaa(callback){
  setTimeout(function(){
    callback( 5 );
  },1000);
}

function done(value){
    alert(value)
}

aaa(function(value){
   done(value);
})

换成JQuery的写法

var defer = $.Deferred(); //构建异步对象

  setTimeout(function(){
    defer.resolve( 5 );
  },1000);

  var filtered  = defer.then(function( value ) {
        return value * 2;
  });

  filtered.done(function( value ) {
      console.log(‘打印出值‘,value)
  });

通过对比我们还是能感觉到很大的不同:

传统的代码逻辑,通过嵌套回调函数,等待异步发送成功的通知后,在执行下一步操作,如果有大量的嵌套回调,耦合度,维护性,扩展性?

那么Deferred对象存在的意义在哪里?

从我角度来说,对于开发者

1:代码可读性,扁平化结构

2:让支离破碎的代码结构,继续保存线性的代码逻辑,也就是异步代码转为同步

3: 从抽线的角度,提供了一个抽象的非阻塞的解决方案(如 Ajax 请求的响应),它创建一个 “promise” 对象,其目的是在未来某个时间点返回一个响应。

deferreds 可以理解为表示需要长时间才能完成的耗时操作的一种方式,相比于阻塞式函数它们是异步的,而不是阻塞应用程序等待其完成然后返回结果。 deferred对 象会立即返回,然后你可以把回调函数绑定到deferred对象上,它们会在异步处理完成后被调用。

所以,总的来讲Deferred通过一组 API 来规范化异步操作,这样也能够让异步操作的流程控制更加容易

下章就是具体的源码分析了,会有附带流程图及源码的分析

类似的异步库可以参考

http://www.cnblogs.com/aaronjs/p/3169328.html

http://www.cnblogs.com/aaronjs/p/3168588.html

基本都大同小异

时间: 2024-10-12 15:23:58

jQuery 2.0.3 源码分析 Deferrred概念的相关文章

转载Aaron ---- jQuery 2.0.3 源码分析core - 选择器

jQuery 2.0.3 源码分析core - 选择器(02) 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 打开jQuery源码,一眼看去到处都充斥着正则表达式,jQuery框架的基础就是查询了,查询文档元素对象,所以狭隘的说呢,jQuery就是一个选择器,并这个基础上构建和运行查询过滤器! 工欲善其事,必先利其器,所以先从正则入手 我们来分解一个表达式 // A simple way to check for HTML strings // Prioritize

转载Aaron博客 ---- jQuery 2.0.3 源码分析core - 整体架构

jQuery 2.0.3 源码分析core - 整体架构 整体架构 拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍 我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧! github上最新是jquery-master,加入了AMD规范了,我就以官方最新2.0.3为准 整体架构 jQuery框架的核心就是从HTML文档中匹配元素并

jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on

转:http://www.cnblogs.com/aaronjs/p/3440647.html?winzoom=1 事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析实现的, 所以需要有一定的基础才行 为了下一步更好的理解内部的实现,所以首先得清楚的认识到事件接口的划分 网上资料遍地都是,但是作为一个jQuery系列的源码分析,我还是很有必要在重新

jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)

转载http://www.cnblogs.com/aaronjs/p/3356505.html Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图************************** **********************源码解析********************** 因为callback被剥离出去后,整个deferred

jQuery 2.0.3 源码分析core - 选择器

转载http://www.cnblogs.com/aaronjs/p/3281911.html 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 打开jQuery源码,一眼看去到处都充斥着正则表达式,jQuery框架的基础就是查询了,查询文档元素对象,所以狭隘的说呢,jQuery就是一个选择器,并这个基础上构建和运行查询过滤器! 工欲善其事,必先利其器,所以先从正则入手 我们来分解一个表达式 // A simple way to check for HTML strin

最细的实现剖析:jQuery 2.0.3源码分析Deferred

Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html **构建Deferred对象时候的流程图** **源码解析** 因为callback被剥离出去后,整个deferred就显得非常的精简 jQuery.extend({ Deferred:function(){} when:function() )}对于extend的继承这个东东,在之前就提及过jquery如何处理内部jquery与init相互引用this的问题 对于JQ的

jQuery 2.0.3 源码分析core - 整体架构

转载http://www.cnblogs.com/aaronjs/p/3278578.html 整体架构 jQuery框架的核心就是从HTML文档中匹配元素并对其执行操作. 例如: $().find().css() $().hide().html('....').hide(). 从上面的写法上至少可以发现2个问题 1. jQuery对象的构建方式 2 .jQuery方法的调用方式 分析一:jQuery的无new构建 JavaScript是函数式语言,函数可以实现类,类就是面向对象编程中最基本的概

jQuery 2.0.3 源码分析 数据缓存

转载http://www.cnblogs.com/aaronjs/p/3370176.html 历史背景: jQuery从1.2.3版本引入数据缓存系统,主要的原因就是早期的事件系统 Dean Edwards 的 ddEvent.js代码 带来的问题: 没有一个系统的缓存机制,它把事件的回调都放到EventTarget之上,这会引发循环引用 如果EventTarget是window对象,又会引发全局污染 不同模块之间用不同缓存变量 一般jQuery开发,我们都喜欢便捷式的把很多属性,比如状态标志

转载Aaron博客 ---- jQuery 2.0.3 源码分析 回调对象 - Callbacks

源码API:http://api.jquery.com/jQuery.Callbacks/ jQuery.Callbacks()是在版本1.7中新加入的.它是一个多用途的回调函数列表对象,提供了一种强大的方法来管理回调函数队列. 那么jQuery.Callbacks使用场景在哪里? 在很多时候需要控制一系列的函数顺序执行.那么一般就需要一个队列函数来处理这个问题 我们看一段代码 function Aaron(List, callback) { setTimeout(function() { va