背景

移动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. HTML5 与 CSS3 jQuery部分知识总结

    一.    HTML5 为什么需要HTML5 什么是HTML5 HTML5现状及浏览器支持 HTML5优点与缺点 HTML5语法规则与文档声明 HTML5新增表达标签 HTML5多媒体组件 HTML5 ...

  2. CS0103: The name ‘Scripts’ does not exist in the current context解决方法

    转至:http://blchen.com/cs0103-the-name-scripts-does-not-exist-in-the-current-context-solution/ 更新:这个bu ...

  3. 弱省互测#0 t2

    题意 给定两个字符串 A 和 B,求下面四个问题的答案: 1.在 A 的子串中,不是 B 的子串的字符串的数量. 2.在 A 的子串中,不是 B 的子序列的字符串的数量. 3.在 A 的子序列中,不是 ...

  4. 向mysql中插入Date类型的数据

    先看数据库表的定义 date字段为sql.date类型.我要向其中插入指定的日期和当前日期. 一.插入当前日期 思路:先获取当前系统,在将当前系统时间转换成sql类型的时间,然后插入数据库.代码如下 ...

  5. Sort with Swap(0, i)

    原题连接:https://pta.patest.cn/pta/test/16/exam/4/question/678 题目如下: Given any permutation of the number ...

  6. CentOS 6.5系统安装配置LAMP(Apache+PHP5+MySQL)服务器环境

    安装篇: 一.安装Apache yum install httpd #根据提示,输入Y安装即可成功安装 /etc/init.d/httpd start#启动Apache 备注:Apache启动之后会提 ...

  7. redis 操作

    redis 1 启动服务 redis-2.8.20 wangchengcheng$ pwd /Users/wangchengcheng/Documents/GameJelly/server/redis ...

  8. SQL语句大全

    经典SQL语句大全(绝对的经典) 一.基础 1.说明:创建数据库CREATE DATABASE database-name 2.说明:删除数据库drop database dbname3.说明:备份s ...

  9. OPENDATASOURCE读取远程数据库数据中文乱码问题-sqlserver R2

    insert into kraft_sync_Store(StoreName,StoreCode,Province,PrefectureCity,CountyCity,Region,Area,Unit ...

  10. 多种坐标系之间的转换 Proj.NET和DotSpatial

    Proj.NET ( http://projnet.codeplex.com/)是一个.NET下开源的空间参照和投影引擎,遵循OGC相关标准.负责人(Coordinators )是D_Guidi 和S ...