背景

移动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));
});
});
}

如何组织代码

但是对于异步+回调的模式,当需要对一系列异步操作进行流程控制的时候似乎必然会面临着回调嵌套。因此怎么把异步操作“拉平”,用更好的方法去优化异步编程的体验,同时也写出更健壮的异步代码,是这两年来前端圈子里很火的话题。

代表的

  1. 消息驱动——代表:@朴灵EventProxy
  2. Promise模式——代表:CommonJS PromisesjQueryDojo
  3. 二次编译——代表:@老赵Jscex
  4. 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的方法等待所有的延时队列加载完毕后,才执行后续代码

使用起来很方便,很精简没有那么多复杂的概念

使用教程之后,下一节附源码的实现

移动web app开发必备 - 异步队列 Deferred的更多相关文章

  1. 移动web app开发必备 - Deferred 源码分析

    姊妹篇  移动web app开发必备 - 异步队列 Deferred 在分析Deferred之前我觉得还是有必要把老套的设计模式给搬出来,便于理解源码! 观察者模式 观察者模式( 又叫发布者-订阅者模 ...

  2. 移动web app开发必备 - zepto事件问题

    问题描述: 项目在祖先元素上绑定了 touchstart,touchmove,touchend事件,用来处理全局性的事件,比如滑动翻页 正常状态下: 用户在子元素上有交互动作时,默认状态下都是会冒泡到 ...

  3. web app 开发必不可少的滑动插件 Flipsnap

    flipsnap.js一个轻量级的滑动效果JS开发库,仅有8k大小(压缩版),包含了10种滑动方式,是web app开发必备的js库,除了兼容主流的智能手机浏览器(iossafari,android, ...

  4. 异步队列 Deferred

    异步队列 Deferred 背景: 移动web app开发,异步代码是时常的事,比如有常见的异步操作: Ajax(XMLHttpRequest) Image Tag,Script Tag,iframe ...

  5. App.js – 用于移动 Web App 开发的 JS 界面库

    App.js 是一个轻量级的 JavaScript UI 库,用于创建像本地应用程序的移动 Web 应用而不牺牲性能和体验.它是跨平台的,特定的UI设计,配置类似原生的过渡效果.App.js 的目的是 ...

  6. 微信公众平台开发:Web App开发入门

    WebApp与Native App有何区别呢?Native App:1.开发成本非常大.一般使用的开发语言为JAVA.C++.Objective-C.2.更新体验较差.同时也比较麻烦.每一次发布新的版 ...

  7. 移动端web app开发学习笔记

    移动web和pc端web以及web app 移动web开发跟web前端开发差别很小,使用的技术都是html+css+js.手机网页可以理解成pc网页的缩小版加一些触摸特性.在浏览器中进行的网页开发,最 ...

  8. Native App开发 与Web App开发(原生与web开发优缺点)

    Native App开发 Native App开发即我们所称的传统APP开发模式(原生APP开发模式),该开发针对IOS.Android等不同的手机操作系统要采用不同的语言和框架进行开发,该模式通常是 ...

  9. 前端读者 | Web App开发入门

    本文来自互联网 自Iphone和Android这两个牛逼的手机操作系统发布以来,在互联网界从此就多了一个新的名词 - Web App(意为基于WEB形式的应用程序).业界关于Web App与Nativ ...

随机推荐

  1. Hoops随便记的

    段 包含图形的段·几何·属性:颜色,可见性,选择功能等等·子段:更低层的段段的名称·段可以进行命名·可以像文件系统一样表示路径:绝对路径.相对路径.通配符当前段(激活的段)·你可以在任何一个时间来处理 ...

  2. Linux Crontab语法

    Crontab语法 Lists 链表值 : 逗号,表示并列,要依次序;Examples:"1,2,5,9", "0-4,8-12". Ranges of num ...

  3. 说说js作用域

    开始就来说说作用域这个蛋疼的东西.里面可能会出现各种的问题 .先给一个简单的例子大家猜猜结果是什么 var   a="b"; function text(){ alert(a);v ...

  4. Linux Shell 编程中的特殊符号

    一.井号 # 1.在脚本文件中对一行进行注释. 2.在引号和\符号后不是注释,只是#号本身: echo "12 # hehe" echo '12 # hehe' echo 12 \ ...

  5. echarts3 -arcgis echarts.js修改

     在echarts.js中修改修改 clone 方法    其中 source instance of Array 修改为Object.prototype.toString.call(source)  ...

  6. STM32之输入捕获以及小小应用(库)

    五一之际,先祝大家五一快乐.其实快乐很简单,工作的人有假放,学习的人也有假放,像我,有假放才有更多的时间学自己想学的东西.51假期学51,可惜没有32假期呀.好了..言归正传,大家听过吸星大法吧..在 ...

  7. Usaco*Monthly Expense

    Description Farmer John是一个令人惊讶的会计学天才,他已经明白了他可能会花光他的钱,这些钱本来是要维持农场每个月的正常运转的.他已经计算了他以后N(1<=N<=100 ...

  8. 执行mysqld_safe报错:mysqld does not exist or is not executable

    执行mysqld_safe报错: [root@edu data]# /usr/local/mysql5.7/bin/mysqld_safe --user=mysql160427 12:41:28 my ...

  9. vim的配置与使用

    经历了一次source insight 一言不合就崩溃之后,决定还是花点时间好好配置和学习以下vim 于是找到大神的配置 https://github.com/humiaozuzu/dot-vimrc ...

  10. TP字母函数

    http://wenku.baidu.com/view/2a0ec9c13c1ec5da50e27099.html?from=search