有些时候,我们需要等待上一个操作完成之后,才能进行下一步的操作。比如Ajax实现自动提交表单操作的时候,程序需要等待,一旦有返回结果了,则继续进行一下步操作。

这时deferred.js这个库就产生了,当然,jquery也有这个功能。下面就分析一下这个库的原理:

/**
* @fileOverview JSDeferred
* @author cho45@lowreal.net
* @version 0.4.0
* @license
* JSDeferred Copyright (c) 2007 cho45 ( www.lowreal.net )
*
* 针对deferred的原理进行精简
*/ ; // no warnings for uglify function Deferred () {
return this.init();
}
//定义静态方法
Deferred.ok = function(x) {return x} //缺省的成功回调
Deferred.ng = function(x) {throw x}
//判断是否为Deferred的实例
Deferred.isDeferred = function (obj) {
return !!(obj && obj._id === Deferred.prototype._id);
}; //这个next是挂在Deferred上的静态方法。与实列方法.next是不同的
Deferred.next = function(fn){
var d = new Deferred();
var img = new Image();
var handler = function(){
d.canceller();
d.calls();
}
//这个地方个人认为比较巧秒,它利用了img加载成功或错误回调具有异步的特性。
//保证完整收集这些.next().next()...
//事实上官方还用了其它两种方式,确保兼容,如setTimeout....
img.addEventListener('error',handler,false);
d.canceller = function(){
img.removeEventListener('error',handler,false);
}
//这里用来触发一个img的加载事件
img.src = "data:image/png," + Math.random();
if(fn) d.callback.ok = fn;
return d;
} //这里是用来模拟一个比较耗时的异步过程
//实践中,可能是取数据的过程,如等待ajax回调
Deferred.wait = function (n) {
var d = new Deferred(), t = new Date();
var id = setTimeout(function () {
d.calls((new Date()).getTime() - t.getTime());
}, n * 1000);
d.canceller = function () { clearTimeout(id) };
return d;
}; Deferred.prototype = {
_id : 8888, //随便填写,用来判断是否为Deferred的实例
init : function(){
this._next = null;
//使Deferred.isDeferred 判断为假
this.callback = {
ok : Deferred.ok,
ng : Deferred.ng
}
return this;
},
next : function (fun) { return this._post("ok", fun) }, calls : function (val) { return this._fire("ok", val) }, _post : function (okng, fun) {
//个人认为,理解这里是关键,
//._next保存一下实例对象,形成一个链
this._next = new Deferred();
this._next.callback[okng] = fun;
return this._next;
}, _fire : function (okng, value) {
var next = "ok"; value = this.callback[okng](value);
//这里的value如果不是Deferred的实例
if (Deferred.isDeferred(value)) {
//加载下一个任务
value._next = this._next;
} else {
//说明没有下一个任务了
if (this._next) this._next._fire(next, value);
}
return this;
}
}

为了配合分析,先给一段测试代码,方便追踪它的流程。

Deferred.next(function(){
alert(1)
return Deferred.wait(3)
}).next(function(){
alert(2)
})

这里用了一个链式写法,熟jquery的人,对此一定不会陌生。首先是调用Deferred.next()这个方法,返回一个Deferred的实例,然后调用实例上的next()方法。

意图是要执行第一个函数(弹出1)之后,再执行第二函数(弹出2),仅管在写法是同步的,但是执行的时候却是异步的。 你或许会问, 为什么不直接用setTimeout呢?

这个问题非常好,其实在很情况下,我觉得确实是可以等价交换。但是setTimeout有一个使用前提,那就是我们必需事先知道上一步运行完成需要等多长时间。而Deferred不需要。

这时你或许又会问,那为什么不用回调呢?这回我终于没有办法说什么了。直接演示一下这种写法:

function(next1,next2,next3,...){
//....
//....
next1(function(){
next2(function(){
next3(...);
})
})
}

像竹笋一样,一层套一层。如果函数体长一点,看着不晕吗?再看看deferred的写法:

Deferred.next(function(){
next1();
}).next(function(){
next2();
}).next(function(){
next3()
}).next(function(){
//...
})

像麻将一样,一字排开,是时髦的链式用法。

如果你觉得怎么写无所谓,那么要结合起来看了,如果next1是一个耗时不确定的操作,但是要保证执行顺序。怎么破?

不管你怎么破,反正我是选择Deferred,用定了。

延时调用--deferred.js原码分析的更多相关文章

  1. basket.js 源码分析

    basket.js 源码分析 一.前言 basket.js 可以用来加载js脚本并且保存到 LocalStorage 上,使我们可以更加精准地控制缓存,即使是在 http 缓存过期之后也可以使用.因此 ...

  2. jqGrid pivot获取所有行包括小计数据及原码分析

    1.结论:按正常jqGid获取,在中间加入以下代码,即将小计行当成改变为普能行,以便能让'getRowData'方法获取到,第三点会进行原码分析 //get all page grid data,in ...

  3. events.js 源码分析

    events.js 源码分析 1. 初始化 // 使用 this.ee = new EventEmitter(); // 源码 // 绑定this域,初始化 _events,_eventsCount和 ...

  4. Require.js 源码分析

    本文将简单介绍下个人对require.js的源码分析,简单分析实现原理 一.require加载资源的流程 require中,根据AMD(Asynchronous Module Definition)的 ...

  5. Backbone.js源码分析(珍藏版)

    源码分析珍藏,方便下次阅读! // Backbone.js 0.9.2 // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. // Backbone ...

  6. Vue.js 源码分析(三十一) 高级应用 keep-alive 组件 详解

    当使用is特性切换不同的组件时,每次都会重新生成组件Vue实例并生成对应的VNode进行渲染,这样是比较花费性能的,而且切换重新显示时数据又会初始化,例如: <!DOCTYPE html> ...

  7. Vue.js 源码分析(三十) 高级应用 函数式组件 详解

    函数式组件比较特殊,也非常的灵活,它可以根据传入该组件的内容动态的渲染成任意想要的节点,在一些比较复杂的高级组件里用到,比如Vue-router里的<router-view>组件就是一个函 ...

  8. Vue.js 源码分析(二十九) 高级应用 transition-group组件 详解

    对于过度动画如果要同时渲染整个列表时,可以使用transition-group组件. transition-group组件的props和transition组件类似,不同点是transition-gr ...

  9. Vue.js 源码分析(二十八) 高级应用 transition组件 详解

    transition组件可以给任何元素和组件添加进入/离开过渡,但只能给单个组件实行过渡效果(多个元素可以用transition-group组件,下一节再讲),调用该内置组件时,可以传入如下特性: n ...

随机推荐

  1. 使用HttpClient来异步发送POST请求并解析GZIP回应

    .NET 4.5(C#): 使用HttpClient来异步发送POST请求并解析GZIP回应 在新的C# 5.0和.NET 4.5环境下,微软为C#加入了async/await,同时还加入新的Syst ...

  2. Javascript的自执行函数

    自执行函数其实也就是"立即执行的函数",它有四个特点:提高性能.利于压缩.避免冲突.依赖加载: 1.减少作用域查找 JS代码: // Anonymous function that ...

  3. IOS UIWebView 下拉刷新功能的简单实现

    1.运行效果图 2.swift 代码的实现 import UIKit class RefreshWebViewController: UIViewController,UIScrollViewDele ...

  4. 关于UIAlertAction如何修改sheet上的字体颜色

    相信很多程序员都会遇到需求是这样的: 但是你发现无论怎么设置cancel和Destructive都无法让红色字体移动到下面取消按钮上: 其实之前一直用错,用了ios9之前的UIActionSheet这 ...

  5. errno

    关于errno有以下需要注意: 1  A common mistake is to do if (somecall() == -1) {                printf("som ...

  6. Web前端开发推荐阅读书籍

    前言 前端工程师在中国兴起也就5年左右,以前公司里没有专门前端工程师的这个职位,很多前端方面的任务都是由全栈工程师来完成,有的基础一点的后台或者设计的帮助分担一些.但是随着互联网的快速发展,特别是所谓 ...

  7. 利用DOS批处理实现定时关机操作

    10月1放假回来,寝室晚上10:30就停电了,最无法让人理解的是第二天早上8:00才来电.原来晚上电脑都是不关机的,开着WiFi一直到天亮,可是现在不行了,电脑如果一直开着第二天早上起来电脑肯定没电, ...

  8. Unity3d使用高通Vuforia发布IOS工程不支持64位的一些解决办法

    1.将Unit升级至4.6.x或5.0.x,将Vuforia差距升级到最新版本(vuforia-unity-mobile-android-ios-4-0-105 ) 2.平台Other Setting ...

  9. Centos 6.5 部署 redmine 3.3

    验证ruby版本 如果有就卸载安装最新的 yum install gcc* openssl openssl-devel -y wget https://ruby.taobao.org/mirrors/ ...

  10. python serial 获取所有的串口名称

    http://blog.csdn.net/qq61394323/article/details/44619511 #!/usr/bin/env python # -*- coding: utf-8 - ...