一, 前言                            

深入学习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, 本文为拜读文章及源码后的笔记,以便日后查阅.

NPO@github

二, 整体脉络                          

对于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实例.

  1. function Promise(executor) {
  2. if (typeof executor != "function") {
  3. throw TypeError("Not a function");
  4. }
  5.  
  6. if (this.__NPO__ !== ) {
  7. throw TypeError("Not a promise");
  8. }
  9.  
  10. // instance shadowing the inherited "brand"
  11. // to signal an already "initialized" promise
  12. this.__NPO__ = ;
  13.  
  14. // 内部结构体
  15. var def = new MakeDef(this);
  16.  
  17. this["then"] = function then(success,failure) {
  18. var o = {
  19. success: typeof success == "function" ? success : true,
  20. failure: typeof failure == "function" ? failure : false
  21. };
  22. // Note: `then(..)` itself can be borrowed to be used against
  23. // a different promise constructor for making the chained promise,
  24. // by substituting a different `this` binding.
  25. o.promise = new this.constructor(function extractChain(resolve,reject) {
  26. if (typeof resolve != "function" || typeof reject != "function") {
  27. throw TypeError("Not a function");
  28. }
  29.  
  30. o.resolve = resolve;
  31. o.reject = reject;
  32. });
  33. // 构建Promise树
  34. def.chain.push(o);
  35.              // 当前Promise节点状态不为pending时,发起异步执行请求事件处理函数
  36. if (def.state !== ) {
  37. schedule(notify,def);
  38. }
  39.  
  40. return o.promise;
  41. };
  42. this["catch"] = function $catch$(failure) {
  43. return this.then(void ,failure);
  44. };
  45.  
  46. try {
  47. // 调用工厂方法
  48. executor.call(
  49. void ,
  50. function publicResolve(msg){
  51. resolve.call(def,msg);
  52. },
  53. function publicReject(msg) {
  54. reject.call(def,msg);
  55. }
  56. );
  57. }
  58. catch (err) {
  59. reject.call(def,err);
  60. }
  61. }

Promise的状态变化放在resolve和reject函数中

  1. function resolve(msg) {
  2. var _then, def_wrapper, self = this;
  3.  
  4. // already triggered?
  5. if (self.triggered) { return; }
  6.  
  7. self.triggered = true;
  8.  
  9. // unwrap
  10. if (self.def) {
  11. self = self.def;
  12. }
  13.  
  14. try {
  15. if (_then = isThenable(msg)) {
  16.           // 构造Thenable的内部状态结构体
  17. def_wrapper = new MakeDefWrapper(self);
  18. _then.call(msg,
  19. function $resolve$(){ resolve.apply(def_wrapper,arguments); },
  20. function $reject$(){ reject.apply(def_wrapper,arguments); }
  21. );
  22. }
  23. else {
  24. self.msg = msg;
  25. self.state = ;
  26. if (self.chain.length > ) {
  27. schedule(notify,self);
  28. }
  29. }
  30. }
  31. catch (err) {
  32. reject.call(def_wrapper || (new MakeDefWrapper(self)),err);
  33. }
  34. }
  35. function reject(msg) {
  36. var self = this;
  37.  
  38. // already triggered?
  39. if (self.triggered) { return; }
  40.  
  41. self.triggered = true;
  42.  
  43. // unwrap
  44. if (self.def) {
  45. self = self.def;
  46. }
  47.  
  48. self.msg = msg;
  49. self.state = ;
  50. if (self.chain.length > ) {
  51. schedule(notify,self);
  52. }
  53. }

下面看一下我觉得最亮眼的地方异步执行请求队列, 主要由以下几个部分组成

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来對队列和异步执行请求作操作.

  1. function notify() {
  2. for (var i=; i<this.chain.length; i++) {
  3. notifyIsolated(
  4. this,
  5. (this.state === ) ? this.chain[i].success : this.chain[i].failure,
  6. this.chain[i]
  7. );
  8. }
  9. this.chain.length = ;
  10. }
  11.  
  12. // NOTE: This is a separate function to isolate
  13. // the `try..catch` so that other code can be
  14. // optimized better
  15. function notifyIsolated(self,cb,chain) {
  16. var ret, _then;
  17. try {
  18. if (cb === false) {
  19. chain.reject(self.msg);
  20. }
  21. else {
  22. if (cb === true) {
  23. ret = self.msg;
  24. }
  25. else {
  26. ret = cb.call(void ,self.msg);
  27. }
  28.  
  29. if (ret === chain.promise) {
  30. chain.reject(TypeError("Promise-chain cycle"));
  31. }
  32. else if (_then = isThenable(ret)) {
  33. _then.call(ret,chain.resolve,chain.reject);
  34. }
  35. else {
  36. chain.resolve(ret);
  37. }
  38. }
  39. }
  40. catch (err) {
  41. chain.reject(err);
  42. }
  43. }
  1. scheduling_queue = (function Queue() {
  2. var first // 指向队首元素
  3. , last // 指向队尾元素
  4. , item;
  5.  
  6. function Item(fn,self) {
  7. this.fn = fn;
  8. this.self = self;
  9. this.next = void ;
  10. }
  11.  
  12. return {
  13. // 元素入队
  14. add: function add(fn,self) {
  15. item = new Item(fn,self);
  16. if (last) {
  17. last.next = item;
  18. }
  19. else {
  20. first = item;
  21. }
  22. last = item;
  23. item = void ;
  24. },
  25. // 清空队列
  26. drain: function drain() {
  27. var f = first;
  28. first = last = cycle = void ;
  29.  
  30. // 从队首元素开始遍历所有队列元素
  31. while (f) {
  32. f.fn.call(f.self);
  33. f = f.next;
  34. }
  35. }
  36. };
  37. })();
  38.  
  39. // 安排执行状态变化事件的处理函数
  40. function schedule(fn,self) {
  41. scheduling_queue.add(fn,self);
  42. // 防止重复发起异步执行请求
  43. if (!cycle) {
  44. cycle = timer(scheduling_queue.drain);
  45. }
  46. }

四, 总结                            

尊重原创,转载请注明来自: http://www.cnblogs.com/fsjohnhuang/p/4293499.html ^_^肥仔John

JS魔法堂: Native Promise Only源码剖析的更多相关文章

  1. JS魔法堂:jsDeferred源码剖析

    一.前言 最近在研究Promises/A+规范及实现,而Promise/A+规范的制定则很大程度地参考了由日本geek cho45发起的jsDeferred项目(<JavaScript框架设计& ...

  2. JS魔法堂:剖析源码理解Promises/A规范

    一.前言 Promises/A是由CommonJS组织制定的异步模式编程规范,有不少库已根据该规范及后来经改进的Promises/A+规范提供了实现 如Q, Bluebird, when, rsvp. ...

  3. JS魔法堂:mmDeferred源码剖析

    一.前言 avalon.js的影响力愈发强劲,而作为子模块之一的mmDeferred必然成为异步调用模式学习之旅的又一站呢!本文将记录我对mmDeferred的认识,若有纰漏请各位指正,谢谢.项目请见 ...

  4. JS魔法堂:IMG元素加载行为详解

    一.前言 在<JS魔法堂:jsDeferred源码剖析>中我们了解到img元素加载失败可以作为函数异步执行的优化方案,本文打算对img元素的加载行为进行更深入的探讨. 二.资源加载的相关属 ...

  5. JS魔法堂:属性、特性,傻傻分不清楚

    一.前言 或许你和我一样都曾经被下面的代码所困扰 var el = document.getElementById('dummy'); el.hello = "test"; con ...

  6. JS魔法堂:判断节点位置关系

    一.前言 在polyfill querySelectorAll 和写弹出窗时都需要判断两个节点间的位置关系,通过jQuery我们可以轻松搞定,但原生JS呢?下面我将整理各种判断方法,以供日后查阅. 二 ...

  7. JS魔法堂:LINK元素深入详解

    一.前言 我们一般使用方式为 <link type="text/css" rel="stylesheet" href="text.css&quo ...

  8. JS魔法堂:追忆那些原始的选择器

    一.前言                                                                                                 ...

  9. Promise的源码实现(完美符合Promise/A+规范)

    Promise是前端面试中的高频问题,我作为面试官的时候,问Promise的概率超过90%,据我所知,大多数公司,都会问一些关于Promise的问题.如果你能根据PromiseA+的规范,写出符合规范 ...

随机推荐

  1. Hello Raspberry Pi

    Raspberry Pi 入手好一段时间了,原意是想撸 linux,但是后来一整年都在忙孩子房子户口本子的事,这玩意也就搁了一年尘. 最近终于被生活折腾到了尾声,开始找一些东西来折腾折腾. 一.什么是 ...

  2. 【腾讯bugly干货分享】解耦---Hybrid H5跨平台性思考

    本文来自于腾讯bugly开发者社区,非经作者同意,请勿转载,原文地址:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=1275& ...

  3. 作业三 代码规范 代码复审 PSP

    1.是否需要有代码规范(5分) 对于是否需要有代码规范,请考虑下列论点并反驳/支持: 1这些规范都是官僚制度下产生的浪费大家的编程时间.影响人们开发效率, 浪费时间的东西. 反对.我并不认为代码规范都 ...

  4. Twain头文件

    #ifndef TWAIN#define TWAIN /************************************************************************ ...

  5. AngularJS快速入门指南19:示例代码

    本文给出的大部分示例都可以直接运行,通过点击运行按钮来查看结果,同时支持在线编辑代码. <div ng-app=""> <p>Name: <input ...

  6. ehcache2拾遗之copyOnRead,copyOnWrite

    问题描述 缓存在提升应用性能,提高访问效率上都是至关重要的一步.ehcache也是广为使用的缓存之一.但是如果将一个可变的对象(如普通的POJO/List/Map等)存入缓存中,会导致怎样潜在的问题. ...

  7. iOS ARC模式 内存管理

     1,测试一 ;i<;i++) { NSLog(@"i = %d",i); } 2,测试二 ;i<;i++) { NSLog(@"i = %d",i ...

  8. js定时器的时间最小值-setTimeout、setInterval

    HTML5标准规定 setTimeout的最短时间间隔是4毫秒: setInterval的最短间隔时间是10毫秒,也就是说,小于10毫秒的时间间隔会被调整到10毫秒 书和MDC 在John Resig ...

  9. FreeMarker 学习

    一.FreeMarker FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页.电子邮件.配置文件.源代码等)的通用工具. 它不是面向最终用户的,而是 ...

  10. [html]三列居中自动伸缩的结构

    html三列居中自动伸缩的结构 <div style="width:100%;height:80px;border:1px solid #DDD;margin-bottom:10px; ...