移动web app开发必备 - 异步队列 Deferred

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介:

背景

移动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的方法等待所有的延时队列加载完毕后,才执行后续代码

 

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

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

  

分类:  Html5, JAVASCRIPT

本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/p/5213944.html ,如需转载请自行联系原作者
相关文章
|
27天前
|
数据库 开发者 Python
web应用开发
【9月更文挑战第1天】web应用开发
38 1
|
15天前
|
数据可视化 图形学 UED
只需四步,轻松开发三维模型Web应用
为了让用户更方便地应用三维模型,阿里云DataV提供了一套完整的三维模型Web模型开发方案,包括三维模型托管、应用开发、交互开发、应用分发等完整功能。只需69.3元/年,就能体验三维模型Web应用开发功能!
37 8
只需四步,轻松开发三维模型Web应用
|
2天前
|
XML 移动开发 前端开发
使用duxapp开发 React Native App 事半功倍
对于Taro的壳子,或者原生React Native,都会存在 `android` `ios`这两个文件夹,而在duxapp中,这些文件夹的内容是自动生成的,那么对于需要在这些文件夹中修改的配置内容,例如包名、版本号、新架构开关等,都通过配置文件的方式配置了,而不需要需修改具体的文件
|
5天前
|
安全 API 开发者
Web 开发新风尚!Python RESTful API 设计与实现,让你的接口更懂开发者心!
在当前的Web开发中,Python因能构建高效简洁的RESTful API而备受青睐,大大提升了开发效率和用户体验。本文将介绍RESTful API的基本原则及其在Python中的实现方法。以Flask为例,演示了如何通过不同的HTTP方法(如GET、POST、PUT、DELETE)来创建、读取、更新和删除用户信息。此示例还包括了基本的路由设置及操作,为开发者提供了清晰的API交互指南。
28 6
|
1天前
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
在快速发展的Web开发领域,高性能与高效响应是衡量应用质量的重要标准。随着Python在Web开发中的广泛应用,如何利用Python的协程(Coroutine)与异步函数(Async Functions)特性来优化Web应用的性能,成为了许多开发者关注的焦点。本文将从实战角度出发,通过具体案例展示如何运用这些技术来提升Web应用的响应速度和吞吐量。
8 1
|
2天前
|
存储 开发工具 Android开发
使用.NET MAUI开发第一个安卓APP
【9月更文挑战第24天】使用.NET MAUI开发首个安卓APP需完成以下步骤:首先,安装Visual Studio 2022并勾选“.NET Multi-platform App UI development”工作负载;接着,安装Android SDK。然后,创建新项目时选择“.NET Multi-platform App (MAUI)”模板,并仅针对Android平台进行配置。了解项目结构,包括`.csproj`配置文件、`Properties`配置文件夹、平台特定代码及共享代码等。
|
4天前
|
存储 JSON API
实战派教程!Python Web开发中RESTful API的设计哲学与实现技巧,一网打尽!
在数字化时代,Web API成为连接前后端及构建复杂应用的关键。RESTful API因简洁直观而广受欢迎。本文通过实战案例,介绍Python Web开发中的RESTful API设计哲学与技巧,包括使用Flask框架构建一个图书管理系统的API,涵盖资源定义、请求响应设计及实现示例。通过准确使用HTTP状态码、版本控制、错误处理及文档化等技巧,帮助你深入理解RESTful API的设计与实现。希望本文能助力你的API设计之旅。
23 3
|
5天前
|
JSON API 数据库
从零到英雄?一篇文章带你搞定Python Web开发中的RESTful API实现!
在Python的Web开发领域中,RESTful API是核心技能之一。本教程将从零开始,通过实战案例教你如何使用Flask框架搭建RESTful API。首先确保已安装Python和Flask,接着通过创建一个简单的用户管理系统,逐步实现用户信息的增删改查(CRUD)操作。我们将定义路由并处理HTTP请求,最终构建出功能完整的Web服务。无论是初学者还是有经验的开发者,都能从中受益,迈出成为Web开发高手的重要一步。
27 4
|
3天前
|
开发框架 JSON 缓存
震撼发布!Python Web开发框架下的RESTful API设计全攻略,让数据交互更自由!
在数字化浪潮推动下,RESTful API成为Web开发中不可或缺的部分。本文详细介绍了在Python环境下如何设计并实现高效、可扩展的RESTful API,涵盖框架选择、资源定义、HTTP方法应用及响应格式设计等内容,并提供了基于Flask的示例代码。此外,还讨论了版本控制、文档化、安全性和性能优化等最佳实践,帮助开发者实现更流畅的数据交互体验。
19 1
|
5天前
|
JSON API 开发者
惊!Python Web开发新纪元,RESTful API设计竟能如此性感撩人?
在这个Python Web开发的新纪元里,RESTful API的设计已经超越了简单的技术实现,成为了一种追求极致用户体验和开发者友好的艺术表达。通过优雅的URL设计、合理的HTTP状态码使用、清晰的错误处理、灵活的版本控制以及严格的安全性措施,我们能够让RESTful API变得更加“性感撩人”,为Web应用注入新的活力与魅力。
19 3