这段时间在学习Promise,但始终不得要领。为了更好地理解Promise,我决定自己实现一个简易版的Promise,以学习Promise工作原理。该工程名为ToyPromise,仓库地址如下:
/pandengyang/toypromise.git
ToyPromise包含了以下属性和方法:
首先,看一下ToyPromise构造函数,代码如下:
function ToyPromise(resolver, name) { this._status = "pending"; this._name = name; this._fullfilled = function dummyFullfilled(value) { return value; }; this._rejected = function dummyRejected(error) { throw error; }; resolver(this._resolve.bind(this), this._reject.bind(this));}
构造函数先进行了一系列的初始化,包括名称、状态、默认的完成/拒绝回调函数,代码如下:
this._status = "pending"; this._name = name; this._fullfilled = function dummyFullfilled(value) { return value; }; this._rejected = function dummyRejected(error) { throw error; };
默认的完成回调函数将完成值重新返回;默认的拒绝回调函数将拒绝原因重新抛出。
初始化后,构造函数立即执行用户传递的resolver函数,并将_resolve和_reject方法传递给resolver。
resolver(this._resolve.bind(this), this._reject.bind(this));
resolver在决议/拒绝该ToyPromise时会调用该ToyPromise的_resolve/_reject方法。代码如下:
var A = new ToyPromise(function resolver(resolve, reject) { var number = Math.random(); if (number <= 0.5) { resolve("a"); } else { reject(new Error("a")); }}, "A");
_resolve方法中的this指向所属ToyPromise,如果直接将this._resolve作为参数传递给resolver,会发生this绑定丢失。this绑定丢失示例如下:
var name = "window";foo = { name: "foo", bar: function bar() { console.log(this.name); }}; foo.bar(); // foovar bar = foo.bar;bar(); // 输出window,而不是foo,说明this丢失了
为了避免this绑定丢失,我们采用硬绑定的方式来传递_resolve和_reject函数。
resolver会根据执行结果_resolve/_reject这个ToyPromise。_resolve/_reject代码如下:
ToyPromise.prototype._resolve = function resolve(value) { if (this._status != "pending") { return; } this._status = "fullfilled"; this._data = value; setTimeout(this._asyncFullfilled.bind(this), 0);};
ToyPromise.prototype._reject = function reject(error) { if (this._status != "pending") { return; } this._status = "rejected"; this._data = error; setTimeout(this._asyncRejected.bind(this), 0);};
首先,检测该ToyPromise是否决议过,这样可以保证ToyPromise只被决议一次,通过then注册的回调也只会执行一次。代码如下:
if (this._status != "pending") { return; }
然后,利用决议结果填充_status与_data。代码如下:
this._status = "fullfilled"; this._data = value;
this._status = "rejected"; this._data = error;
最后,利用setTimeout异步调用通过then注册的_fullfilled/_rejected回调函数。代码如下:
setTimeout(this._asyncFullfilled.bind(this), 0);
setTimeout(this._asyncRejected.bind(this), 0);
如果不异步调用_fullfilled/_rejected,当在resolver中同步调用_resolve/_reject时,会出现回调函数调用过早的问题。例如:
var A = new ToyPromise(function resolver(resolve, reject) { var number = Math.random(); if (number <= 0.5) { resolve("a"); } else { reject(new Error("a")); }}, "A");A.then(/* callbacks */)
resolver同步调用_resolve/_reject,此时,ToyPromise的构造函数还未返回,A.then也未调用。也就是说,用户还未注册回调函数,回调函数(默认的)就已经运行了。由于ToyPromise只能被决议一次,A.then注册的回调函数永远也不会运行。
现在看一下then方法,then方法用于向ToyPromise注册完成/拒绝回调函数。代码如下:
ToyPromise.prototype.then = function(fullfilled, rejected, name) { this._fullfilled = fullfilled; this._rejected = rejected; nextPromise = new ToyPromise(function resolver(resolve, reject) {}, name); this._nextPromise = nextPromise; console.log("" + this._name + "'s nextPromise is " + name); return nextPromise;};
首先,为当前ToyPromise注册_fullfilled/_rejected函数,代码如下:
this._fullfilled = fullfilled;this._rejected = rejected;
然后,创建一个新的ToyPromise,并让当前ToyPromise指向这个新的ToyPromise,然后返回该ToyPromise。代码如下:
nextPromise = new ToyPromise(function resolver(resolve, reject) {}, name); this._nextPromise = nextPromise; console.log("" + this._name + "'s nextPromise is " + name); return nextPromise;
当一个ToyPromise被决议时,除了执行_fullfilled/_rejected方法外,还需要决议由then返回的ToyPromise,以此实现链式调用。因此,当ToyPromise调用then时,使用_nextPromise指向由then返回的ToyPromise。代码如下:
this._nextPromise = nextPromise;
由此,形成了一条ToyPromise链,示意图如下:
图中,A调用then时,会为A注册完成/拒绝处理函数,然后A指向A.then返回的B。B调用then时,会为B注册完成/拒绝处理函数,然后B指向B.then返回的C。从而形成一条ToyPromise链。
当ToyPromise被决议/拒绝时,会调用_resolve/_reject方法,然后异步调用_asyncFullfilled/_asyncRejected方法。_asyncFullfilled/_asyncRejected方法代码如下:
ToyPromise.prototype._asyncFullfilled = function() { console.log("" + this._name + " " + this._status + ": " + this._data); if (!this._nextPromise) { return; } var result; try { console.log("call " + this._name + "'s _fullfiled"); result = this._fullfilled(this._data); } catch (error) { console.log("reject next promise " + this._nextPromise._name + " by exception" ); this._nextPromise._reject(error);return; } if (result instanceof ToyPromise) { result._fullfilled = this._nextPromise._fullfilled; result._rejected = this._nextPromise._rejected; result._nextPromise = this._nextPromise._nextPromise;this._nextPromise = result;console.log("" + this._name + "'s next promise is " + this._nextPromise._name ); console.log("" + result._name + "'s next promise is " + result._nextPromise._name ); } else { console.log("resolve next promise " + this._nextPromise._name); this._nextPromise._resolve(result); }};
ToyPromise.prototype._asyncRejected = function() { console.log("" + this._name + " " + this._status + ": " + this._data); if (!this._nextPromise) { return; } var result; try { console.log("call " + this._name + "'s _rejected"); result = this._rejected(this._data); } catch (error) { console.log("reject next promise " + this._nextPromise._name + "by exception" ); this._nextPromise._reject(error);return; } if (result instanceof ToyPromise) { result._fullfilled = this._nextPromise._fullfilled; result._rejected = this._nextPromise._rejected; result._nextPromise = this._nextPromise._nextPromise;this._nextPromise = result;console.log("" + this._name + "'s next promise is " + this._nextPromise._name ); console.log("" + result._name + "'s next promise is " + result._nextPromise._name ); } else { console.log("resolve next promise " + this._nextPromise._name); this._nextPromise._resolve(result); }};
首先,检测该ToyPromise是否调用过then,调用了then之后,_nextPromise会被赋值。代码如下:
if (!this._nextPromise) { return;}
其次,执行用户注册的_fullfilled/_rejected函数。若执行过程中没有异常,则使用返回值决议_nextPromise;若执行过程中catch到异常,则使用该异常作为拒绝原因拒绝_nextPromise。代码如下:
var result; try { console.log("call " + this._name + "'s _fullfiled"); result = this._fullfilled(this._data); } catch (error) { console.log("reject next promise " + this._nextPromise._name + " by exception" ); this._nextPromise._reject(error);return; } if (result instanceof ToyPromise) { } else { console.log("resolve next promise " + this._nextPromise._name); this._nextPromise._resolve(result); }
try { console.log("call " + this._name + "'s _rejected"); result = this._rejected(this._data); } catch (error) { console.log("reject next promise " + this._nextPromise._name + "by exception" ); this._nextPromise._reject(error);return; } if (result instanceof ToyPromise) { } else { console.log("resolve next promise " + this._nextPromise._name); this._nextPromise._resolve(result); }
从代码中我们可以看出,ToyPromise链是交叉运行,并不是说第一个ToyPromise是fullfilled的,其后跟着的所有ToyPromise都是fullfilled。
若完成/拒绝回调函数返回的是一个ToyPromise,则返回的ToyPromise会替换掉由then返回的ToyPromise。代码如下:
if (result instanceof ToyPromise) { result._fullfilled = this._nextPromise._fullfilled; result._rejected = this._nextPromise._rejected; result._nextPromise = this._nextPromise._nextPromise;this._nextPromise = result;console.log("" + this._name + "'s next promise is " + this._nextPromise._name ); console.log("" + result._name + "'s next promise is " + result._nextPromise._name ); }
完成/拒绝回调函数返回ToyPromise的示意图如下:
最初,A.then返回了B,A的_nextPromise指向B。若A的_fullfilled返回了B_1,则将A的_nextPromise修正为B_1,B_1的_fullfilled、_rejected、_nextPromise分别指向B的同名字段。当B_1被决议时,会重新触发ToyPromise链的运行。
ToyPromise仅仅用于演示Promise的基本工作原理,没有考虑封装以及Promise的一些偏高级的用法。
如果觉得《自己动手写cpu pdf_自己动手写 Promise》对你有帮助,请点赞、收藏,并留下你的观点哦!