JS魔法堂: Native Promise Only源码剖析
一, 前言
深入学习Promise的朋友应该都看过<深入理解Promise五部曲>这一系列的文章, 以解除回调地狱之外的观点来剖析Promise更多的内涵,确实十分精彩.
Part 1: The Sync Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000586666)
Part 2: The Inversion Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000591382)
Part 3: The Trust Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000593885)
Part 4: The Extension Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000600268)
Part 5: The LEGO Problem(译文:http://segmentfault.com/blog/kk_470661/1190000000611040)
NPO(Native Promise Only)是原文作者polyfill的ES6 Promise, 本文为拜读文章及源码后的笔记,以便日后查阅.
二, 整体脉络
对于Promise实现而言, 主要的主体类型就两个-----Promise和Thenable. NPO中通过MakeDef构建Promise的内部状态结构体def, 并且通过def.chain存储Promise子节点P2-1,P2-2到P2-n, 从而形成一颗Promise树. 而Thenable的内部状态结构体def_wrapper则由MakeDefWrapper构建而成.
Promise树的结构并不稳定, 实际上每个Promise节点仅与状态为pending的子节点关联, 一旦子节点状态发生变化则断开关联.(该部分在 notify() 中实现)
{Promise} then(success, failure) , 将success和failure事件处理函数与新生成的Promise子节点绑定, 但订阅的是Promise父节点的状态变化事件.
另外NPO中通过构建一个异步执行请求队列(scheduling_queue),来收集异步执行请求然后對请求作同一处理,并通过门闩(cycle)来防止重复执行异步请求处理操作.
三, 源码详解
先看看Promise构造函数, 规定仅能通过new方式来构建Promise实例.
function Promise(executor) {
if (typeof executor != "function") {
throw TypeError("Not a function");
} if (this.__NPO__ !== ) {
throw TypeError("Not a promise");
} // instance shadowing the inherited "brand"
// to signal an already "initialized" promise
this.__NPO__ = ; // 内部结构体
var def = new MakeDef(this); this["then"] = function then(success,failure) {
var o = {
success: typeof success == "function" ? success : true,
failure: typeof failure == "function" ? failure : false
};
// Note: `then(..)` itself can be borrowed to be used against
// a different promise constructor for making the chained promise,
// by substituting a different `this` binding.
o.promise = new this.constructor(function extractChain(resolve,reject) {
if (typeof resolve != "function" || typeof reject != "function") {
throw TypeError("Not a function");
} o.resolve = resolve;
o.reject = reject;
});
// 构建Promise树
def.chain.push(o);
// 当前Promise节点状态不为pending时,发起异步执行请求事件处理函数
if (def.state !== ) {
schedule(notify,def);
} return o.promise;
};
this["catch"] = function $catch$(failure) {
return this.then(void ,failure);
}; try {
// 调用工厂方法
executor.call(
void ,
function publicResolve(msg){
resolve.call(def,msg);
},
function publicReject(msg) {
reject.call(def,msg);
}
);
}
catch (err) {
reject.call(def,err);
}
}
Promise的状态变化放在resolve和reject函数中
function resolve(msg) {
var _then, def_wrapper, self = this; // already triggered?
if (self.triggered) { return; } self.triggered = true; // unwrap
if (self.def) {
self = self.def;
} try {
if (_then = isThenable(msg)) {
// 构造Thenable的内部状态结构体
def_wrapper = new MakeDefWrapper(self);
_then.call(msg,
function $resolve$(){ resolve.apply(def_wrapper,arguments); },
function $reject$(){ reject.apply(def_wrapper,arguments); }
);
}
else {
self.msg = msg;
self.state = ;
if (self.chain.length > ) {
schedule(notify,self);
}
}
}
catch (err) {
reject.call(def_wrapper || (new MakeDefWrapper(self)),err);
}
}
function reject(msg) {
var self = this; // already triggered?
if (self.triggered) { return; } self.triggered = true; // unwrap
if (self.def) {
self = self.def;
} self.msg = msg;
self.state = ;
if (self.chain.length > ) {
schedule(notify,self);
}
}
下面看一下我觉得最亮眼的地方异步执行请求队列, 主要由以下几个部分组成
1. notify, 遍历def.chain中的所有Promise子节点, 最后由于所有Promise子节的状态均变为fulfilled或rejected因此清空def.chain.
2. notifyIsolated, 被notify所调用, 用于单独调用绑定在每个Promise子节点的success或failure事件处理函数, 并修改Promse子节点的状态.
3. scheduling_queue, 存放异步执行请求(以链表实现, 對队列首尾操作性能比数组高).
4. schedule, 向异步执行请求队列添加元素, 并发起异步请求处理操作.
上述的1和2两点将作为异步执行请求被存放在3中.代码中各部分则通过4来對队列和异步执行请求作操作.
function notify() {
for (var i=; i<this.chain.length; i++) {
notifyIsolated(
this,
(this.state === ) ? this.chain[i].success : this.chain[i].failure,
this.chain[i]
);
}
this.chain.length = ;
} // NOTE: This is a separate function to isolate
// the `try..catch` so that other code can be
// optimized better
function notifyIsolated(self,cb,chain) {
var ret, _then;
try {
if (cb === false) {
chain.reject(self.msg);
}
else {
if (cb === true) {
ret = self.msg;
}
else {
ret = cb.call(void ,self.msg);
} if (ret === chain.promise) {
chain.reject(TypeError("Promise-chain cycle"));
}
else if (_then = isThenable(ret)) {
_then.call(ret,chain.resolve,chain.reject);
}
else {
chain.resolve(ret);
}
}
}
catch (err) {
chain.reject(err);
}
}
scheduling_queue = (function Queue() {
var first // 指向队首元素
, last // 指向队尾元素
, item; function Item(fn,self) {
this.fn = fn;
this.self = self;
this.next = void ;
} return {
// 元素入队
add: function add(fn,self) {
item = new Item(fn,self);
if (last) {
last.next = item;
}
else {
first = item;
}
last = item;
item = void ;
},
// 清空队列
drain: function drain() {
var f = first;
first = last = cycle = void ; // 从队首元素开始遍历所有队列元素
while (f) {
f.fn.call(f.self);
f = f.next;
}
}
};
})(); // 安排执行状态变化事件的处理函数
function schedule(fn,self) {
scheduling_queue.add(fn,self);
// 防止重复发起异步执行请求
if (!cycle) {
cycle = timer(scheduling_queue.drain);
}
}
四, 总结
尊重原创,转载请注明来自: http://www.cnblogs.com/fsjohnhuang/p/4293499.html ^_^肥仔John
JS魔法堂: Native Promise Only源码剖析的更多相关文章
- JS魔法堂:jsDeferred源码剖析
一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计& ...
- JS魔法堂:剖析源码理解Promises/A规范
一.前言 Promises/A是由CommonJS组织制定的异步模式编程规范,有不少库已根据该规范及后来经改进的Promises/A+规范提供了实现 如Q, Bluebird, when, rsvp. ...
- JS魔法堂:mmDeferred源码剖析
一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...
- JS魔法堂:IMG元素加载行为详解
一.前言 在<JS魔法堂:jsDeferred源码剖析>中我们了解到img元素加载失败可以作为函数异步执行的优化方案,本文打算对img元素的加载行为进行更深入的探讨. 二.资源加载的相关属 ...
- JS魔法堂:属性、特性,傻傻分不清楚
一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...
- JS魔法堂:判断节点位置关系
一.前言 在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅. 二 ...
- JS魔法堂:LINK元素深入详解
一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css&quo ...
- JS魔法堂:追忆那些原始的选择器
一.前言 ...
- Promise的源码实现(完美符合Promise/A+规范)
Promise是前端面试中的高频问题,我作为面试官的时候,问Promise的概率超过90%,据我所知,大多数公司,都会问一些关于Promise的问题.如果你能根据PromiseA+的规范,写出符合规范 ...
随机推荐
- 微信公共平台开发-(.net实现)1--成为开发者
刚换了个新环境,哎这都快一个月了,还没适应过来,还是怀念老地方呀.老板让开发一个基于微信平台的开发项目,而且是用net实现.当时就蒙了,微信就用了一会个人赶脚不好,所以果断不用,现在让开发,而且查了一 ...
- Metrics-Java版的指标度量工具之二
接上<Metrics-Java版的指标度量工具之一> 4. Histograms Histograms主要使用来统计数据的分布情况,最大值.最小值.平均值.中位数,百分比(75 ...
- Java多线程11:ReentrantLock的使用和Condition
ReentrantLock ReentrantLock,一个可重入的互斥锁,它具有与使用synchronized方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大. Reentran ...
- Android获取View对应的Bitmap
我的应用里面有一个需求,将一个画面分享出去,这个画面底层是一个View,所以首先要把这个View转换成Bitmap,然后在分享这个bitmap即可.话不多说,直接上代码. 有个地方需要注意一下:就是/ ...
- HyperDock,让Mac的窗口飞
三年前写了一篇文章,介绍了Windows 7下的Aero效果,其实最终是想引出来写Mac OS上能产生类似功能的HyperDock应用程序,可惜这一拖,就拖到连Windows 10都快要发布了.没有关 ...
- 虚拟化平台cloudstack(5)——参考资料
虚拟化的几种方式 完全虚拟化: 半虚拟化: 硬件辅助虚拟化: 详细的内容可以看: http://pan.baidu.com/share/link?shareid=4134188256&uk=2 ...
- Node.js与Sails~Model数据模型
回到目录 对于Sails来说,它的Model与数据库对应,不过它并没有采用目前比较流行的poco贫血模型,而是采用了类似DDD的充血模型,即它的数据实体里即有数据库字段(属性)而且还有方法,而模型里的 ...
- EF架构~数据分批批量提交
回到目录 对于大数据量提交,包括插入,更新和删除,我始终不建议用EF自带的方法,因为它会增加与数据库的交互次数,一般地,EF的一个上下文在提交时会打开一个数据连接,然后把转换成的SQL语句一条一条的发 ...
- Atitit 马尔可夫过程(Markov process) hmm隐马尔科夫。 马尔可夫链,的原理attilax总结
Atitit 马尔可夫过程(Markov process) hmm隐马尔科夫. 马尔可夫链,的原理attilax总结 1. 马尔可夫过程1 1.1. 马尔科夫的应用 生成一篇"看起来像文章的 ...
- iOS-网络爬虫
1.iOS开发——网络实用技术OC篇&网络爬虫-使用青花瓷抓取网络数据 2.iOS开发——网络使用技术OC篇&网络爬虫-使用正则表达式抓取网络数据 3.iOS—网络实用技术OC篇&am ...