移动web app开发必备 - 异步队列 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)就是一个异步的过程,如果同时执行多个查询,势必同步代码要等待数据查询结束后调用

附项目源码:执行多次异步查询


1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

/**

  * 初始化操作

  * @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 库,例如  QDojo, 和  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的方法等待所有的延时队列加载完毕后,才执行后续代码

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

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

时间: 2024-12-04 20:37:28

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

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

flipsnap.js一个轻量级的滑动效果JS开发库,仅有8k大小(压缩版),包含了10种滑动方式,是web app开发必备的js库,除了兼容主流的智能手机浏览器(iossafari,android,win phone7 ie9+)外还兼容 PC 版的谷歌和Safari等. 演示地址:http://pxgrid.github.io/js-flipsnap/demo.html web app 开发必不可少的滑动插件 Flipsnap,布布扣,bubuko.com

Web高效开发必备的PHP框架

Web高效开发必备的PHP框架项目名称:多功能THinkPHP开源框架项目名称:基于Laravel的轻量级web部署系统Fixhub 1.项目名称:多功能 THinkPHP 开源框架 项目简介:使用 THinkPHP 开发项目的过程中把一些常用的功能或者第三方 sdk 整合好,开源供亲们参考,如 Auth 权限管理.支付宝.微信支付.阿里oss.友盟推送.融云即时通讯.云通讯短信.Email.Excel.PDF 等等.这些都是经过线上运营考验的,无毒害可以免费放心折腾使用.只要不会某一天找到我说

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

Native App开发 Native App开发即我们所称的传统APP开发模式(原生APP开发模式),该开发针对IOS.Android等不同的手机操作系统要采用不同的语言和框架进行开发,该模式通常是由“云服务器数据+APP应用客户端”两部份构成,APP应用所有的UI元素.数据内容.逻辑框架均安装在手机终端上. Web App开发 Web App开发即是一种框架型APP开发模式(HTML5  APP 框架开发模式),该开发具有跨平台的优势,该模式通常由“HTML5云网站+APP应用客户端”两部份

web app开发技巧总结

Web APP开发技巧总结 一.META/LINK相关: 1.百度禁止转码 通过百度手机打开网页时,百度可能会对你的网页进行转码,往你页面贴上它的广告,非常之恶心.不过我们可以通过这个meta标签来禁止它: <meta http-equiv="Cache-Control" content="no-siteapp" /> 相关链接:SiteApp 转码声明 2.添加到主屏后的标题(IOS) <meta name="apple-mobile-

web app开发——使用jQuery Mobile创建餐厅订餐应用

从零开始创建移动Web应用需要编程者学习专门的移动编程知识技能,还要有比如专门的编码工具(比如专门为iOS系统开发应用的工具),再有就是需要在每一个平台都进行测试部署(如Andr??oid,iOS,BlackBerry,Windows Mobile等). 这样的话,开发移动应用将花费不少,而另一个解决方案是再使用HTML,CSS,Javascript,它们的特点是跨平台和跨设备.此外,目前已 经出现了不少专为移动web应用开发的专门框架,比如jQuery Mobile.jQuery Mobile

WEB APP 开发标签

第一个meta标签表示:强制让文档的宽度与设备的宽度保持1:1,并且文档最大的宽度比例是1.0,且不允许用户点击屏幕放大浏览: 第二个meta标签是iphone设备中的safari私有meta标签,它表示:允许全屏模式浏览: 第三个meta标签也是iphone的私有标签,它指定的iphone中safari顶端的状态条的样式: 第四个meta标签表示:告诉设备忽略将页面中的数字识别为电话号码 <meta content=”width=device-width, initial-scale=1.0,

前端读者 | Web App开发入门

本文来自互联网 自Iphone和Android这两个牛逼的手机操作系统发布以来,在互联网界从此就多了一个新的名词 - Web App(意为基于WEB形式的应用程序).业界关于Web App与Native App的争论已有一段时间,而Hybrid混合App则受到推荐,随着时间的推移,我们相信Web App也会有一定的市场,那么它到底有什么奥秘呢?让我们来看看. Web App与Native App有何区别呢? NativeApp 开发成本非常大.一般使用的开发语言为JAVA.C++.Objecti

jquery 1.7.2源码解析(四) 异步队列Deferred Object

异步队列Deferred Object 一)jQuery.Callbacks( flags ) 1.总体结构 该函数返回一个链式工具对象(回调函数列表),用于管理一组回调函数. 2.源码分析 1.工具函数createFlags(flags) 该函数用于将字符串标记转换为对象格式标记,并把转换结果缓存起来. // String to Object flags format cache var flagsCache = {}; // Convert String-formatted flags in

【Web APP开发】阅读资料总结

内容整理自网络,不足之处欢迎指出 Webapp:是一个针对Iphone.Android优化后的web站点,它使用的技术无非就是HTML或HTML5.CSS3.Javas cript,服务端技术JAVA.PHP.ASP.针对高端智能手机(如Iphone.Android)做站点适配,并非是针对普通手机开发Wap 2.0 从APP到WAP版,在产品上,最明显且核心的:1.精简功能,只将核心的任务实现,非核心的枝节可考虑删减.2.做好新的WebAPP导航.3.补充从WebAPP 对 下载原生APP 的引