异步队列 Deferred
异步队列 Deferred
背景:
移动web app开发,异步代码是时常的事,比如有常见的异步操作:
- Ajax(XMLHttpRequest)
- Image Tag,Script Tag,iframe(原理类似)
- setTimeout/setInterval
- CSS3 Transition/Animation
- HTML5 Web Database
- postMessage
- Web Workers
- Web Sockets
- and more…
后面几个是CSS3 HML5加入的新API.这些接口都是会产生异步的操作
比如本人的一个phonegap项目,操作HTML5本地数据库(HTML5 Web Database)就是一个异步的过程,如果同时执行多个查询,势必同步代码要等待数据查询结束后调用
附项目源码:执行多次异步查询
/**
* 初始化操作
* @return
*/
proto.initProcess =
function
(){
var
self =
this
,
prev =
null
,
curr =
null
,
next =
null
;
debug.group(
"start of init process"
);
var
idx = self.chapterIndex;
debug.info(
"PageBase: 执行初始化之前的操作!"
);
self.initProcessBefore();
if
(idx == 0){
debug.info(
"PageBase: 初始化入口点从第一章开始进入"
);
debug.info(
"PageBase: 解析器解析第一章数据!"
);
curr = self.process(self.chapters[idx]);
curr.then(
function
(pages){
debug.info(self.format(
"PageBase: 第一章数据解析完成,解析页面数为{0}"
, pages.length));
self.cPages = pages;
if
(self.isChangeFont){
self.idx = Math.ceil((pages.length - 1) * self.idx);
}
self.cPages.idx = idx;
/////////////////////////////////////////////////
//
// 2013.1.10修改
// 如果只有一个章节的情况下
//
if
(1 === self.chapters.length){
deferred.all([curr]).then(self.steup.bind(self));
}
else
{
debug.info(
"PageBase:解析器解析后一章数据!"
);
next = self.loadNextData(idx + 1);
next.then(
function
(args){
debug.info(self.format(
"PageBase: 后一章数据解析完成,解析页面数为{0}"
, args.pages.length));
self.nPages = args.pages;
self.nPages.idx = idx + args.index;
debug.info(self.format(
"PageBase: 初始化数据解析完成, 当章索引{0} 当章页数{1} 下章索引{2} 下章页数{3}"
, self.cPages.idx , self.cPages.length , self.nPages.idx , self.nPages.length));
debug.info(
"PageBase: 初始化数据解析完成,即将生成结构操作!"
);
});
deferred.all([curr , next]).then(self.steup.bind(self));
}
});
}
else
if
(idx == self.chapters.length -1){
debug.info(
"PageBase: 初始化入口点从最后一章开始进入"
);
debug.info(
"PageBase:解析器解析最后一章数据!"
);
prev = self.loadPrevData(idx - 1);
prev.then(
function
(args){
self.pPages = args.pages;
self.pPages.idx = args.index + 1;
debug.info(self.format(
"PageBase: 最后一章的前一章数据解析完成,解析页面数为{0}"
, args.pages.length));
curr = self.process(self.chapters[idx]);
curr.then(
function
(pages , data){
if
(self.isChangeFont){
self.idx = Math.ceil((pages.length - 1) * self.idx);
}
self.cPages = pages ;
self.cPages.idx = idx;
debug.info(self.format(
"PageBase: 最后一章数据解析完成,解析页面数为{0}"
, pages.length));
debug.info(self.format(
"PageBase: 初始化数据解析完成, 前章索引{0} 前章页数{1} 当章索引{2} 当章页数{3} "
, self.pPages.idx , self.pPages.length , self.cPages.idx , self.cPages.length ));
debug.info(
"PageBase: 初始化数据解析完成,即将生成结构操作!"
);
});
deferred.all([prev , curr]).then(self.steup.bind(self));
});
}
else
{
debug.info(
"PageBase: 初始化入口点从中间章开始进入"
);
prev = self.loadPrevData(idx - 1);
debug.info(
"PageBase:解析器解析中间章的前一章数据!"
);
prev.then(
function
(args){
self.pPages = args.pages ;
self.pPages.idx = args.index;
debug.info(self.format(
"PageBase: 中间章前一章数据解析完成,解析页面数为{0}"
, args.pages.length));
debug.info(
"PageBase:解析器解析中间章数据!"
);
curr = self.process(self.chapters[idx]);
curr.then(
function
(pages , data){
if
(self.isChangeFont){
self.idx = Math.ceil((pages.length) * self.idx);
// console.log("spages.length - 1",pages.length)
// console.log("self.idx",self.idx)
}
self.cPages = pages ;
self.cPages.idx = idx;
debug.info(self.format(
"PageBase: 中间章数据解析完成,解析页面数为{0}"
,pages.length));
debug.info(
"PageBase:解析器解析中间章的后一章数据!"
);
next = self.loadNextData(idx + 1);
next.then(
function
(args){
self.nPages = args.pages ;
self.nPages.idx = idx + args.index;
debug.info(self.format(
"PageBase: 中间章后一章数据解析完成,解析页面数为{0}"
, args.pages.length));
debug.info(self.format(
"PageBase: 初始化数据解析完成, 前章索引{0} 前章页数{1} 当章索引{2} 当章页数{3} 下章索引{4} 下章页数{5}"
, self.pPages.idx , self.pPages.length , self.cPages.idx , self.cPages.length , self.nPages.idx , self.nPages.length));
debug.info(
"PageBase: 初始化数据解析完成,即将生成结构操作!"
)
});
deferred.all([prev , curr , next]).then(self.steup.bind(self));
});
});
}
如何组织代码
但是对于异步+回调的模式,当需要对一系列异步操作进行流程控制的时候似乎必然会面临着回调嵌套。因此怎么把异步操作“拉平”,用更好的方法去优化异步编程的体验,同时也写出更健壮的异步代码,是这两年来前端圈子里很火的话题。
代表的
- 消息驱动——代表:@朴灵 的EventProxy
- Promise模式——代表:CommonJS Promises,jQuery,Dojo
- 二次编译——代表:@老赵 的Jscex
- jQuery 是唯一的实现了这种 when 方法的库。其他的 promises 库,例如 Q, Dojo, 和 when 依照 Promises/B spec 实现了 when 方法, 但是并没有实现注释者提及的 when 方法。但是,Q 库有一个 all方法,when.js 也有一个 parallel方法,与上面的 jQuery.when 方法作用一样,只是它们接受一个数组类型的参数,而不是任意数量的参数。
回顾Jquery Deferred
- 从1.5版本开始,jQuery加入了Deferred功能,让事件处理队列更加的完善。并用 这个机制重写了Ajax模块。虽然还没轮到Ajax,但是接下来的事件处理函数中牵扯到了 这个机制
- Deferred把回调函数注册到一个队列中,统一管理,并且可以同步或者异步地调用 这些函数。jQuery.Deferred()用来构造一个Deferred对象。该对象有状态值,共有三种: Rejected, Resolved和初始状态。其中Resolved表示该操作成功完成了,而Rejected 则表示出现了错误,调用失败。Deferred对象的主要成员如下:
- done(callback): 注册一个callback函数,当状态为resolved时被调用。
- fail(callback): 注册一个callback函数,当状态为rejected时被调用。
- always(callback): 注册一个callback函数,无论是resolved或者rejected都会被 调用。
- then(successCallback, failureCallback): 同时传入成功和失败的回调函数。
- pipe(successFilter, failureFilter): 在调用成功和失败的回调函数前先调用pipe 指定的函数。算是一种管道机制,拦截了函数调用。
- resolve(args): 把状态设置为Resolved。
- reject(args): 把状态设置为Rejected。
- promse(): 返回的是一个不完整的Deferred的接口,没有resolve和reject。即不能 修改Deferred对象的状态。可以看作是一种只读视图。这是为了不让外部函数提早触发 回调函数。比如$.ajax在1.5版本后不再返回XMLHttpRequest,而是返回一个封装了 XMLHttpRequest和Deferred对象接口的object。其中Deferred部分就是promise()得到 的,这样不让外部函数调用resolve和reject,防止在ajax完成前触发回调函数。把这 两个函数的调用权限保留给ajax内部。
deferred-js
本人在项目中使用 Promise/A 规范实现的 deferred-js , 比较简单轻巧.
如何使用?
API:
var DeferredAPI = { deferred : deferred, all : all, Deferred : Deferred, DeferredList : DeferredList, wrapResult : wrapResult, wrapFailure : wrapFailure, Failure : Failure } |
最简单常用的案例

- //Deferred对象创建
- var d = new deferred.Deferred()
- //添加一个回调到递延的回调链
- d.then(function(result) {
- console.log('Hello ' + result)
- return result
- })
- //等待回调后触发
- d.resolve('World')

每个链接在一个回调链可以是两个函数,代表一个成功,一个失败
只有一个成功回调
- d.then(function(result) {
- // 自己的代码
- return result
- })
失败回调
- d.fail(function(failure) {
- // optionally do something useful with failure.value()
- return failure
- });
添加一个成功方法和一个失败方法

- d.then(function(result) {
- // do something useful with the result
- return result
- }, function(failure) {
- // optionally do something useful with failure.value()
- return failure
- })

不管回调成功或者失败都执行同一份代码

- d.both(function(result) {
- // in the case of failure, result is a Failure
- // do something in either case
- return result
- })

如果许多异步在操作,比如提供的案例,在要执行HTML5数据库N次后,如何操作呢?
请仔细对照下案例中的
- deferred.all([prev , curr , next]).then(self.steup.bind(self));
all的方法等待所有的延时队列加载完毕后,才执行后续代码
使用起来很方便,很精简没有那么多复杂的概念
使用教程之后,下一节附源码的实现
异步队列 Deferred的更多相关文章
- 移动web app开发必备 - 异步队列 Deferred
背景 移动web app开发,异步代码是时常的事,比如有常见的异步操作: Ajax(XMLHttpRequest) Image Tag,Script Tag,iframe(原理类似) setTimeo ...
- jQuery源码分析(九) 异步队列模块 Deferred 详解
deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/grea ...
- jQuery 源码解析(八) 异步队列模块 Callbacks 回调函数详解
异步队列用于实现异步任务和回调函数的解耦,为ajax模块.队列模块.ready事件提供基础功能,包含三个部分:Query.Callbacks(flags).jQuery.Deferred(funct) ...
- .Net中的并行编程-4.实现高性能异步队列
上文<.Net中的并行编程-3.ConcurrentQueue实现与分析>分析了ConcurrentQueue的实现,本章就基于ConcurrentQueue实现一个高性能的异步队列,该队 ...
- jquery ajax 对异步队列defer与XMLHttprequest.onload的依赖
ajax 对异步队列defer与XMLHttprequest.onload的依赖
- [js高手之路]javascript腾讯面试题学习封装一个简易的异步队列
这道js的面试题,是这样的,页面上有一个按钮,一个ul,点击按钮的时候,每隔1秒钟向ul的后面追加一个li, 一共追加10个,li的内容从0开始技术( 0, 1, 2, ....9 ),首先我们用闭包 ...
- .Net中的并行编程-7.基于BlockingCollection实现高性能异步队列
三年前写过基于ConcurrentQueue的异步队列,今天在整理代码的时候发现当时另外一种实现方式-使用BlockingCollection实现,这种方式目前依然在实际项目中使用.关于Blockin ...
- 基于异步队列的生产者消费者C#并发设计
继上文<<基于阻塞队列的生产者消费者C#并发设计>>的并发队列版本的并发设计,原文code是基于<<.Net中的并行编程-4.实现高性能异步队列>>修改 ...
- js异步队列之理解
起因 最近看到一篇关于js异步执行顺序的解答,觉得有所收获,遂记录下来. marcotask和microtask js中异步队列可以分为两类,marcotask队列和microtask队列, marc ...
随机推荐
- 登录记住账号和密码小Demo
读取 // 1.读取沙盒中plist文件 // 1.1.获得沙盒根路径 NSString *home = NSHomeDirectory(); // 1.2.拼接Documents路径 NSStrin ...
- IOS中TableView的使用(1) -创建一个简单的tableView
创建一个简单的tableView: #import <UIKit/UIKit.h> /*tableView 一定要遵守这两个协议: UITableViewDataSource,UITabl ...
- python元类分析
刚開始接触到Python新式类中的元类的概念的时候非常是纠结了下..不知道这是个啥东西... 用下面几个定义来说明吧: (1)Python中,类也是对象..仅仅只是这样的对象比較的特殊,他用于创建别的 ...
- MVC验证03-自定义验证规则、禁止输入某些值
原文:MVC验证03-自定义验证规则.禁止输入某些值 本文继续体验自定义验证规则,需求是禁止输入某些值.本文与前2篇相关,请参考:MVC验证01-基础.远程验证 MVC验证02-自定义验证规则.邮 ...
- .NET:从 Mono、.NET Core 说起
魅力 .NET:从 Mono..NET Core 说起 前段时间,被问了这样一个问题:.NET 应用程序是怎么运行的? 当时大概愣了好久,好像也没说出个所以然,得到的回复是:这是 .NET 程序员最基 ...
- MySQL中group_concat函数-和group by配合使用
MySQL中group_concat函数 完整的语法如下: group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔 ...
- Ubuntu12.10无法安装openssh-server[已解决]
因为要在Ubuntu下搞些东西,家里的台式有Deepin2013,但是发现有很多依赖的问题,实在不想解决,就到win7下用VBox安装了Ubuntu.打算使用SourceCRT连接虚拟机,但是在安装在 ...
- C#放缩、截取、合并图片并生成高质量新图的类
原文:C#放缩.截取.合并图片并生成高质量新图的类 using System;using System.Drawing;using System.Drawing.Imaging;using Syste ...
- Android--扫描二维码
http://www.cnblogs.com/keyindex/archive/2011/06/08/2074900.html
- 基于.NET Socket Tcp的发布-订阅框架
基于.NET Socket Tcp的发布-订阅框架 一.分布式消息总线 在很多MIS项目之中都有这样的需求,需要一个及时.高效的的通知机制,即比如当使用者A完成了任务X,就需要立即告知使用者B任务X已 ...