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
基本都大同小异
jQuery 2.0.3 源码分析 Deferrred概念的更多相关文章
- jQuery 2.0.3 源码分析 Deferred概念
JavaScript编程几乎总是伴随着异步操作,传统的异步操作会在操作完成之后,使用回调函数传回结果,而回调函数中则包含了后续的工作.这也是造成异步编程困难的主要原因:我们一直习惯于“线性”地编写代码 ...
- jQuery 2.0.3 源码分析Sizzle引擎解析原理
jQuery 2.0.3 源码分析Sizzle引擎 - 解析原理 声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 先来回答博友的提问: 如何解析 div > p + ...
- jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)
Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图* ...
- 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,谢谢! 先来回答博友的提问: 如何解析 div > p + div.aaron input[type="checkb ...
- jQuery 2.0.3 源码分析Sizzle引擎 - 编译函数(大篇幅)
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 从Sizzle1.8开始,这是Sizzle的分界线了,引入了编译函数机制 网上基本没有资料细说这个东东的,sizzle引入这 ...
随机推荐
- SQL中添加远程服务器连接
EXEC sp_addlinkedserver 'Testserver','','SQLOLEDB','192.168.1.221' EXEC sp_addlinkedsrvlogin 'Testse ...
- Gradle的配置实例
错过了Maven,但是遇到了Gradle. 网上关于Gradle的讲解和培训已经很多了. 我就直接贴几个我测试过的配置文件吧: ① 依赖maven资源库 repositories { mavenCen ...
- 统计fastq文件中读段的数量
mycount=`cat SRR108114_new_1.fastq | wc -l` echo 'Number of clean reads, SRR108114_new_1.fastq: '$(( ...
- sql注入在线检测(sqlmapapi)
版权:http://blog.csdn.net/yueguanghaidao/article/details/38026431 每次看都不方便 摘抄下来 之前一搞渗透的同事问我,sqlmapapi ...
- CSS定位小技巧
CSS定位Static 默认定位Relative 相对定位:left 和topposition: relative;/*相对定位*/ left:40px;/*在原来的位置向右移动*/ top:100p ...
- [SAP ABAP开发技术总结]EXIT-COMMAND
声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将 ...
- Apache-Shiro+Zookeeper系统集群安全解决方案之缓存管理
上篇[Apache-Shiro+Zookeeper系统集群安全解决方案之会话管理],解决了Shiro在系统集群开发时安全的会话共享问题,系统在使用过程中会有大量的权限检查和用户身份检验动作,为了不频繁 ...
- XAF应用开发教程(四)应用程序模型
XAF是重量型框架,确实够重量的,方方面面都做得规规矩矩. 如果看了前面三节,可能会认为,这N多的Attribute到底都是从哪里来的?到底有多少这样的Attribute?如果不够用了怎么办?等着官方 ...
- 装了maven插件的eclipse中M2_REPO无法编辑、删除(转)
今天用了新版本的eclipse,用maven在命令行生成了一个普通项目.导入eclipse之后发现本地仓库的路径不正确. 显示的为 user.path/.m2/repository 但是我的仓库早已经 ...
- [转载] HTTP 协议漫谈
原文: http://blog.jobbole.com/88199/ 简介 网络上已经有不少介绍HTTP的的好文章.对HTTP的一些细节介绍的比较好,所以本篇文章不会对HTTP的细节进行深究,而是从够 ...