JSDeferred 源码分析
不经意看到了一个构思非常惊人的异步流程控制库,发出来分享下
http://cho45.stfuawsc.com/jsdeferred/
关于CommonJS Promises请看另一个异步库 http://www.cnblogs.com/aaronjs/p/3168588.html
整个代码非常简洁,易用,不过呢是小日本写的东西…
API手册:
加载jsdeferred定义延迟对象。为方便起见,我们用Deferred.define()方法把接口导出到全局作用于中
Deferred.define();
通过这样做,你就能使用如 next(), loop(), call(), parallel() and wait() 这样的全局函数方法,让我们抒写一些异步的流程
next(function () { alert("Hello!"); return wait(5); }). next(function () { alert("World!"); });
这个流程中,开始会弹出 “Hello”,然后过5秒接着会弹出 “world”
Deferred.next(function () { alert("Hello!"); return Deferred.wait(5); }). next(function () { alert("World!"); });
上面是抒写同上
个人分析:
用Deferred.define()方法,无疑污染了全局作用域,入侵性太强了,跟mootools,prototype一样, 不过好处嘛,很明显,简单易用了
还好JSDeferred也提供了无侵入的写法
亮源码:
Deferred.define = function (obj, list) { if (!list) list = Deferred.methods; if (!obj) obj = (function getGlobal() { return this })(); for (var i = 0; i < list.length; i++) { var n = list[i]; obj[n] = Deferred[n]; } return Deferred; };
可以传入一个对象,用作上下文
var o = {}; //定义一个对象 Deferred.define(o);//把Deferred的方法加持到它上面,让o成为一个Deferred子类。 o.next(function(){ /* 处理 */ })
Deferred.methods = ["parallel", "wait", "next", "call", "loop", "repeat", "chain"];
next方法
- next方法是可以进行链式操作,链式的原理很简单就是要返回当前的的this上下文才可以
- 所以很明显第一个next我们必须要建一个上下文对象提供给后面next引用,其实跟jquery链式一个道理
根据源码看来Deferred.next其实就是一个静态方法
Deferred.next = Deferred.next_faster_way_readystatechange || Deferred.next_faster_way_Image || Deferred.next_tick || Deferred.next_default;
显而易见,next方法是有四种选择优先级,为什么要这样呢?
- 目的是用于提供了一个JSDeferred实例与实现第一个异步操作。
- 异步操作在JSDeferred中有许多实现方法,如setTimeout,img.onerror或 script.onreadystatechange ,
- 它会视浏览器选择最快的异步方式
我是基于webkit的游览器,所以我们就直接看Deferred.next_faster_way_Image
// Modern Browsers var d = new Deferred(); var img = new Image(); var handler = function () { d.canceller(); d.call(); }; img.addEventListener("load", handler, false); img.addEventListener("error", handler, false); d.canceller = function () { img.removeEventListener("load", handler, false); img.removeEventListener("error", handler, false); }; img.src = "data:image/png," + Math.random(); if (fun) d.callback.ok = fun; return d;
流程:
- 创建一个Deferred对象
- 创建Image对象,实现异步
- 监听事件
- if (fun) d.callback.ok = fun; 放置回调处理对象
- 返回当前deferred对象
由此可见
next(fn).next(fn).next(fn)
其实就是
第一个 next() Deferred.next_faster_way_Image () 返回 d
第二个 next() d.next()
第二个next(),其实就是实例Deferred类的原型方法了
具体我们看
next: function (fun) { return this._post("ok", fun) },
_post: function (okng, fun) { this._next = new Deferred(); this._next.callback[okng] = fun; return this._next; },
看到_post方法,就有拨开云雾见月明的感觉了
其实内部会有重新生成一个 Deferred对象挂到父实例的 next上,在绑定回调..
如此依次循环处理
所以初始化的时候其实内部就生成了这么一个队列
_next 上都挂着下一个队列处理
此时都是在初始化准备好的了,在执行的时候 Image 在成功回调中,我们调用了 d.call();
执行实例的call方法
call: function (val) { return this._fire("ok", val) },
_fire: function (okng, value) { var next = "ok"; try { value = this.callback[okng].call(this, value); } catch (e) { next = "ng"; value = e; if (Deferred.onerror) Deferred.onerror(e); } if (Deferred.isDeferred(value)) { value._next = this._next; } else { if (this._next) this._next._fire(next, value); } return this; }
- 取出当前实例的回调方法,传入参数
- 返回的value 是否还是一个isDeferred对象
- 如果没有返回值,继续调用内部的_next上的Deferred实例,依次循环
总结:
构思很特别,把每次链式的回调挂到内部的next子属性中,在处理上也保持了一条线的引用关系,而不是常规的用数组的方式存起来
当然上面仅仅只是同步方法的处理分析,也不是标准的遵循CommonJS Promises规范去抒写的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!