转载http://www.cnblogs.com/aaronjs/p/3348569.htmlcss
JavaScript编程几乎老是伴随着异步操做,传统的异步操做会在操做完成以后,使用回调函数传回结果,而回调函数中则包含了后续的工做。这也 是形成异步编程困难的主要缘由:咱们一直习惯于“线性”地编写代码逻辑,可是大量异步操做所带来的回调函数,会把咱们的算法分解地支离破碎。此时咱们不能 用if来实现逻辑分支,也不能用while/for/do来实现循环,更不用提异步操做之间的组合、错误处理以及取消操做了。所以也诞生了如jQuery Deferred这样的辅助类库html
举一个例子会有助于理解java
我作的是phonegap项目,因此涉及到的异步处理就别特多:jquery
1. 常见的setTimeout算法
2. 与底层代码通讯,调用插件cordova.exec数据库
3. postmessage与iframe通讯编程
4. CSS3 动画api
5. Ajaxpromise
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回调会在处于如下两种不一样的状态下执行:
'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是如何实现的?
因为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
基本都大同小异