jQuery 2.0.3 源码分析 Deferred概念
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
基本都大同小异
jQuery 2.0.3 源码分析 Deferred概念的更多相关文章
- jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)
Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...
- jQuery 2.0.3 源码分析 Deferrred概念
转载http://www.cnblogs.com/aaronjs/p/3348569.html JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而 ...
- jQuery 2.0.3 源码分析Sizzle引擎解析原理
jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...
- jQuery 2.0.3 源码分析core - 选择器
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 打开jQuery源码,一眼看去到处都充斥着正则表达式,jQuery框架的基础就是查询了,查询文档元素对象 ...
- jQuery 2.0.3 源码分析 事件绑定 - bind/live/delegate/on
事件(Event)是JavaScript应用跳动的心脏,通过使用JavaScript ,你可以监听特定事件的发生,并规定让某些事件发生以对这些事件做出响应 事件的基础就不重复讲解了,本来是定位源码分析 ...
- jQuery 2.0.3 源码分析 事件体系结构
那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...
- jQuery 2.0.3 源码分析core - 整体架构
拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery ...
- jQuery 2.0.3 源码分析Sizzle引擎 - 编译函数(大篇幅)
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 从Sizzle1.8开始,这是Sizzle的分界线了,引入了编译函数机制 网上基本没有资料细说这个东东的,sizzle引入这 ...
- jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + div.aaron input[type="checkb ...
随机推荐
- Ajax工作流程
Ajax通过XMLHttpRequest对象实现异步方式在后台发送发送请求. 主要有以下四个步骤: (1)初始化XMLHttpRequest对象.不同浏览器的差异,需要我们创建一个跨浏览器的对象,并判 ...
- Day 2:增加SplashScreen
If you want to add just single image, then create a pic in the size of 480*800 and name it as Splash ...
- springmvc2 一个控制器写多个方法(非注解方式)
出处:http://blog.csdn.net/xuewenke/article/details/23895999 springmvc2 一个控制器写多个方法(非注解方式) 分类: spring 20 ...
- HTML5
一.头部设置 <!--页面窗口自动调整到设备宽度,并禁止用户及缩放页面--> <meta name="viewport" content="width= ...
- 未能加载文件或程序集“Antlr3.Runtime”或它的某一个依赖项
清空编译临时文件夹,从新编译就行了,路径如下: C:/Users/hp/AppData/Local/Temp/Temporary ASP.NET Files
- [IOS] 'Double' is not convertible to 'CGFloat'
在做一个对象旋转的时候,要求转动的弧度角, 这个地方报错,如题的错误,其实是类型转换的问题,swift不能静静的做类型转换,一定要显式的转换 typeTableView?.transform=CGAf ...
- Win10 下安装 NodeJS
1,右键点击底部导航栏win(开始)弹出,使用 命令提示符(管理员A) 2,输入命令,进入安装文件目录,输入 msiexec/package node-v4.4.4-x64.msi ----弹出安 ...
- Smart3D系列教程6之 《案例实战演练3——倾斜数据正射影像及DSM的生产》
一.前言 Wish3D出品的系列教程中,前面两讲分别讲述说明了小物件的照片三维重建.大区域地形的三维重建,从照片的直接导入至软件到通过Excel表格将区块导入处理,从不同的模型类别.不同的导入方式演示 ...
- outline (group) 在Excel worksheet 中
Group按钮的作用就是使Excel能展示一个轮廓,将明晰列折上,只显示公式的结果列. 在代码中实现的方法: 用worksheet的get_range选中明晰columns的某行单元格,然后调用这个r ...
- matlab 采样函数
dyaddown 功能:对时间序列进行二元采样,每隔一个元素提取一个元素,得到一个降采样时间序列. 格式: 1.y = dyaddown(x, EVENODD) 当EVENODD=0时,从x中第二个元 ...