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

简介:

在分析Deferred之前我觉得还是有必要把老套的设计模式给搬出来,便于理解源码!

观察者模式

观察者模式( 又叫发布者-订阅者模式 )应该是最常用的模式之一.

它定义了一种一对多的关系让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使得它们能够自动更新自己。

使用观察者模式的好处:

  1. 支持简单的广播通信,自动通知所有已经订阅过的对象。
  2. 页面载入后目标对象很容易与观察者存在一种动态关联,增加了灵活性。
  3. 目标对象与观察者之间的抽象耦合关系能够单独扩展以及重用。
  • 在这种模式中,有两类对象,分别是“观察者-Observer”和“目标对象-Subject”。
  • 目标对象中保存着一份观察者的列表,当目标对象的状态发生改变的时候就主动向观察者发出通知(调用观察者提供的方法),从而建立一种发布/订阅的关系。
  • 这一种发布/订阅的关系常用于实现事件、消息的处理系统。

观察者模式类图

image

参与者

  • Subject(抽象主题)
  • 能够知道它自己的观察者,若干观察者对象可能监视一个主题对象;
  • 提供一个接口,用来附加和取消观察者对象;
  • Observer(抽象观察者)
  • 它为对象定义了一个(自我)更新的接口,并且当主题对象发生改变的时候能够被通知;
  • ConcreteSubject(具体主题)
  • 存储具体观察者感兴趣的状态;
  • 当它的状态改变的时候,通知它所有的观察者对象;
  • ConcreteObserver(具体观察者)
  • 持有一个具体主题的引用;
  • 存储和主题对象一致的状态,并且该能够保留该状态;
  • 实现抽象观察者的Update接口,以便能够同主题对象的状态保持一致;

工作方式

  • 当某个具体主题状态发生改变的时候,通知它的所有观察者对象,以便保证这些观察者对象的状态同它的状态保持一致;
  • 当被告知具体主题对象发生改变,一个具体观察者对象可能会去查询主题对象,以便获得一个消息;该观察者使用这个消息来使它的状态同主题对象的状态保持一致;

观察者模式序列图

image

观察者对象初始化变化请求,它不会立即更新,直到通过主题对象的Notify方法来获得一个变化的通知。主题的Notify方法并非总是被主题对象调用,它也能够被观察者对象调用,或者完全被其他不同类型的对象调用。

Deferred怎样工作

  • 可以创建一个Deferred对象直接,但你通常请求从一个数据源
  • 数据源是一个函数,返回一个延迟对象
  • 延迟对象提供了访问数据源的数据,允许你把成功回调函数/或失败函数其回调链
  • 当数据源有结果,它将在延时对象上调用resolve(result)方法或者在失败的情况下调用reject(failure)方法,这导致延迟对象的回调链被释放了——这意味着每个链接链(回调或errback)被称为反过来,结果是输入到第一个回调,它的输出是输入到下一个回调(等等)
  • 如果一个callback (or errback) 返回一个 Failure对象,那么下一个就是errback调用,是否就是callback调用

Deferred-process

 

Deferred整体结构

Deferred的定义:

  • Deferred是基于Promises/A,Promises是一种异步编程模型,通过一组API来规范化异步操作,这样也能够让异步操作的流程控制更加容易
  • 由于Promises对于新手而言理解曲线还是比较陡峭的,这里循序渐进的给大家介绍,同时实现一个最简单的Promises/A代码
  • Promises/A有个别名叫做“thenable”,就是“可以then”的。这里一个promise有三种状态:[默认、完成、失败],初始创建的时候是默认状态,状态只可以从默认变成完成,或者默认变成失败。一旦完成或者失败,状态就不能再变定义的接口

Deferred提供的API

2
3
4
5
6
7
8
9
10
var  DeferredAPI = {
   deferred     : deferred,
   all          : all,
   Deferred     : Deferred,
   DeferredList : DeferredList,
   wrapResult   : wrapResult,
   wrapFailure  : wrapFailure,
   Failure      : Failure
}

案例分析一

我们通过简单的demo来解析程序的执行流程

2
3
4
5
6
7
8
9
10
11
12
function  asynchronous(delay,name) {
     var  d = new  deferred.Deferred()
     setTimeout( function () {
          d.resolve( '执行' +name)
     }, delay || 1000);
     return  d
};
var  d1 = new  asynchronous(1000, 'd1' );
d1.then( function (result){
     alert(result) //结果是 执行d1
})
  • 执行asynchronous方法,传入参数
  • new deferred.Deferred() 用来构造一个Deferred对象
  • setTimeout 模拟异步执行操作
  • d.resolved表示该操作成功完成了
  • 返回Deferred对象
  • then 增加成功回调函数和失败回调函数到各自的队列中便捷方法,两个参数可以是数组或null
  • setTimeout 执行完毕后出发resolved方法
  • 执行then注册的回调函数
  • 执行结束

源码实现

通过查看源码就能发现,其实Deferred.js的官方API不清晰,内部还有很多接口的实现没有说明

内部实现的处理器就2个模块,分别是:

  • 处理单个异步 Deferred 类
  • 处理多个异步 DeferredList 类

 

Deferred 类

 new deferred.Deferred()   
 deferred.deferred()

二着是等同的,最终的实现是Deferred类,每次实例化一次就是一个新是上下文

Deferred在构造之后,相对的实例就具有了以下方法:

  • cancel
  • then
  • fail
  • both
  • resolve
  • reject
  • pause
  • unpause
  • inspect
  • thenReturn
  • thenCall
  • failReturn
  • failCall

Deferred.then方法

来,订阅一个
    Deferred.prototype.then = function (callback, errback) {
        this.callbacks.push({callback: callback, errback: errback})
        if (this.called) _run(this)
        return this
    }

可以直接传入二个回调函数,分别对应都 done|fail 二个状态的回调, 跟 $.jquery还是有区别,可以直接传入三个回调函数,分别对应done|fail|progress三个状态的回调 

用this.callbakcs 存储具体观察者感兴趣的内容

Deferred.resolve方法

发布通知
  Deferred.prototype.resolve = function(result) {
      _startRun(this, result)
      return this
  }

代码中间有很多逻辑判断,我们暂且先跳过,看看最终的执行方法

  View Code

运行流程

  1. 其中d就是传入的异步对象了
  2. d.callbacks就是订阅收集的具体观察者感兴趣的内容,也就是callback or errback
  3. 匹配出正确的回调方法,执行
  4. d.result = fn(d.result) 传入回调函数需要的参数
  5. 单一的流程就执行完毕了

 

我们看看复杂点的构造

案例分析二

等待许多异步数据源

2
3
4
5
6
7
8
9
10
11
12
13
14
15
function  asynchronous(delay,name) {
     var  d = new  deferred.Deferred()
     setTimeout( function () {
          d.resolve( '执行' +name)
     }, delay || 1000);
     return  d
};
 
var  d1 = new  asynchronous(2000, 'd1' );
var  d2 = new  asynchronous(3000, 'd2' );
 
deferred.all([d1, d2]).then( function (result){
      console.log(result)   //["执行d1", "执行d2"]
}).thenCall(console.log(11111));

执行流程:

  • 等待在一个列表的所有值的递延的对象,或者开始一群延迟操作和运行回调链当第一个成功还是失败了
  • 等待d1, d2都加载完毕后接受到所有的values后,deferred.all运行作用域链,当然也一样可以提供成功或者失败
  • 返回的result将会是数组格式的 convert ['a', 'b', 'c'] to 'abc'

或者传入配置参数:

fireOnFirstResult: true  只要返回第一个d1处理

fireOnFirstError: true   只返回第一个错误的处理

2
3
4
deferred.all([d1, d2],{fireOnFirstResult: true }).then( function (result){
      console.log(result)   //["执行d1"]
}).thenCall(console.log(11111));

最后看2个API没有给出的接口

2
3
dAll.failCall(console.error)
dAll.then(join).thenCall(console.log)

案例分析三

任意组合模式

2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function  asynchronous(delay,name) {
      var  d = new  deferred.Deferred()
      setTimeout( function () {
           d.resolve( '执行' +name)
      }, delay || 1000);
      return  d
  };
 
  var  d1 = new  asynchronous(2000, 'd1' );
 
var  d = d1.then( function (result1) {
     var  d2 = new  asynchronous(200, 'd2' );
     return  d2.then( function (result2) {
         return   result
     })
}).then( function (data) {
     console.log(data)
     return  data
})

执行流程:

  • 一个回调函数的返回值被传递到下一个回调。
  • 当一个回调返回一个延迟对象,原来的延迟将透明地等待其他接收它的值,然后运行它自己的回调链使用该值。我们把这种叫做嵌套。

总结:

  • 现在我们可以用deferred对象做很多事了,返回它,将它传递给另一个函数,等。
  • 随后的回调注册在deferred将收到返回的值从最内层的回调:result1+ result2。
  • 用起来是不是很爽!
本文转自艾伦 Aaron博客园博客,原文链接:http://www.cnblogs.com/aaronjs/p/3169328.html,如需转载请自行联系原作者
相关文章
|
1天前
|
设计模式 测试技术 持续交付
开发复杂Web应用程序
【10月更文挑战第3天】开发复杂Web应用程序
6 2
|
3天前
|
Java PHP
PHP作为广受青睐的服务器端脚本语言,在Web开发中占据重要地位。理解其垃圾回收机制有助于开发高效稳定的PHP应用。
【10月更文挑战第1天】PHP作为广受青睐的服务器端脚本语言,在Web开发中占据重要地位。其垃圾回收机制包括引用计数与循环垃圾回收,对提升应用性能和稳定性至关重要。本文通过具体案例分析,详细探讨PHP垃圾回收机制的工作原理,特别是如何解决循环引用问题。在PHP 8中,垃圾回收机制得到进一步优化,提高了效率和准确性。理解这些机制有助于开发高效稳定的PHP应用。
16 3
|
2天前
|
JavaScript 前端开发 小程序
uniapp一个人开发APP关键步骤和考虑因素
uniapp一个人开发APP关键步骤和考虑因素
13 0
uniapp一个人开发APP关键步骤和考虑因素
|
1月前
|
数据可视化 图形学 UED
只需四步,轻松开发三维模型Web应用
为了让用户更方便地应用三维模型,阿里云DataV提供了一套完整的三维模型Web模型开发方案,包括三维模型托管、应用开发、交互开发、应用分发等完整功能。只需69.3元/年,就能体验三维模型Web应用开发功能!
66 8
只需四步,轻松开发三维模型Web应用
|
14天前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
|
17天前
|
XML 移动开发 前端开发
使用duxapp开发 React Native App 事半功倍
对于Taro的壳子,或者原生React Native,都会存在 `android` `ios`这两个文件夹,而在duxapp中,这些文件夹的内容是自动生成的,那么对于需要在这些文件夹中修改的配置内容,例如包名、版本号、新架构开关等,都通过配置文件的方式配置了,而不需要需修改具体的文件
|
21天前
|
安全 API 开发者
Web 开发新风尚!Python RESTful API 设计与实现,让你的接口更懂开发者心!
在当前的Web开发中,Python因能构建高效简洁的RESTful API而备受青睐,大大提升了开发效率和用户体验。本文将介绍RESTful API的基本原则及其在Python中的实现方法。以Flask为例,演示了如何通过不同的HTTP方法(如GET、POST、PUT、DELETE)来创建、读取、更新和删除用户信息。此示例还包括了基本的路由设置及操作,为开发者提供了清晰的API交互指南。
80 6
|
18天前
|
存储 开发工具 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`配置文件夹、平台特定代码及共享代码等。
|
20天前
|
存储 JSON API
实战派教程!Python Web开发中RESTful API的设计哲学与实现技巧,一网打尽!
在数字化时代,Web API成为连接前后端及构建复杂应用的关键。RESTful API因简洁直观而广受欢迎。本文通过实战案例,介绍Python Web开发中的RESTful API设计哲学与技巧,包括使用Flask框架构建一个图书管理系统的API,涵盖资源定义、请求响应设计及实现示例。通过准确使用HTTP状态码、版本控制、错误处理及文档化等技巧,帮助你深入理解RESTful API的设计与实现。希望本文能助力你的API设计之旅。
46 3
|
21天前
|
JSON API 数据库
从零到英雄?一篇文章带你搞定Python Web开发中的RESTful API实现!
在Python的Web开发领域中,RESTful API是核心技能之一。本教程将从零开始,通过实战案例教你如何使用Flask框架搭建RESTful API。首先确保已安装Python和Flask,接着通过创建一个简单的用户管理系统,逐步实现用户信息的增删改查(CRUD)操作。我们将定义路由并处理HTTP请求,最终构建出功能完整的Web服务。无论是初学者还是有经验的开发者,都能从中受益,迈出成为Web开发高手的重要一步。
44 4