先直接上源码吧。es6
if(!window.Promise) { function Promise(fn) { var self=this; this.status = 'pending'; this.thenCache = []; this.count = 0 if(!(this instanceof Promise)) { throw 'Defer is a constructor and should be called width "new" keyword'; } if(typeof fn !== 'function') { throw 'Defer params must be a function'; } //为了让传进来的函数在then后执行 setTimeout(function() { try { fn.call(this, self.resolve.bind(self), self.reject.bind(self)) } catch(e) { self.reject(e); } }, 0); } Promise.prototype.resolve = function(value) { this.value = value; this.status = 'resolved'; this.triggerThen(); } Promise.prototype.reject = function(reason) { this.value = reason; this.status = 'rejected'; this.triggerThen(); } Promise.prototype.then = function(onResolve, onReject) { this.thenCache.push({ onResolve: onResolve, onReject: onReject }); console.log('this', this) return this; } Promise.prototype.catch = function(fn) { if(typeof fn === 'function') { this.errorHandle = fn; } }; Promise.prototype.triggerThen = function() { console.log('this.thenCache', this.thenCache) console.log('this.status', this.status) var current = this.thenCache.shift(), res; console.log('current', current) if(!current && this.status === 'resolved') { // console.log('--11--', + new Date()) return this; } else if(!current && this.status === 'rejected') { if(this.errorHandle) { this.value = this.errorHandle.call(undefined, this.value); this.status = 'resolved'; console.log('--11--', + new Date()) } return this; }; if(this.status === 'resolved') { // console.log('--222--', + new Date()) res = current.onResolve; } else if(this.status === 'rejected') { console.log('--222--', + new Date()) res = current.onReject; } this.count ++; if(typeof res === "function") { try { this.value = res.call(undefined, this.value); this.status = 'resolved'; console.log('-ddd--', + new Date()) this.triggerThen(); // console.log('this.count', this.count, + new Date()) } catch(e) { this.status = 'rejected'; this.value = e; return this.triggerThen(); } } else { console.log('--44--') this.triggerThen(); } } window.Promise = Promise; }
以前写过如何构造一个promise库,参考的美团点评的网站,写到后面发现Promise.resolve()直接调用就会出现问题。报错上面的库,也是引用Talking Coder的一篇博客,逐渐开始理解promise内部是如何实现的了,固然仍是有一些疑问。好比上面的库,须要注释window.Promise = Promise和if(!window.Promise) {} 内部的代码在debug的时候才能看得见。但若是注释掉,直接Promise.resolve()又回一样报resolve不是一个方法。promise
阮一峰在promise基础篇提到过,阮一峰promise基础介绍,then返回的是一个新的Promise实例,不是原来的那个实例。这里提到的原来的那个实例,我想应该是第一次实例化的Promise实例对象。这里有点不懂,为什么then函数在return this后,就会返回一个新的Promise实例对象。浏览器
大概讲解下此Promise库的原理吧。函数
setTimeout(function() { try { fn.call(this, self.resolve.bind(self), self.reject.bind(self)) } catch(e) { self.reject(e); } }, 0);
call 和 bind 都是为了绑定this的指向,由于直接回调,this在浏览器里面指向的是window对象,绑定fn执行的时候this指向Promise的实例对象。同理绑定resolve和reject的this指向。
setTimout 0秒后,是为了让fn在队列的最后执行。这里的队列一会再剖析。或者说让resolve或reject在队列的最后执行。
若是去掉setTimeout 0秒后,那么在实例化Promise的时候,就会马上执行回调fn,进而执行resolve或reject函数,而此时then还将来得及push须要thenAble的回调队列,致使再执行resolve或reject里面的triggerThen()方法时,没法执行(此时回调队列为空。)网站
Promise.prototype.then = function(onResolve, onReject) { this.thenCache.push({ onResolve: onResolve, onReject: onReject }); return this; }
then 实际是一个回调队列,当有3个then,那么回调队列就会有三个。而后通this.triggerThen()
以此递归调用,直接回调队列被调用完毕后,再执行fn中的resolve或reject函数。this
再分析下状态和返回值
在triggerThen函数里有这么几行代码prototype
if(typeof res === "function") { try { this.value = res.call(undefined, this.value); this.status = 'resolved'; this.triggerThen(); } catch(e) { this.status = 'rejected'; this.value = e; return this.triggerThen(); } } else { this.triggerThen(); }
能够看出,当js无语法报错的时候,执行的try代码。此时将then()中第一个回调函数执行的value赋值给了this.value。在new Promise()的回调函数中,若是执行过resolve()的话,那么此时赋值就是第二次赋值了。同理this.status。当出现语法报错的时候,会执行catch,此时将错误参数e一样赋值给this.value。状态也被变为rejected。同理若是在new Promise()回调中执行过reject,那么此时赋值也是第二次赋值了。这就验证了阮一峰在Promise基础介绍将的下面的代码逻辑。debug
const promise = new Promise(function(resolve, reject) { throw new Error('test'); }); promise.catch(function(error) { console.log(error); }); // Error: test
catch 跟then的第二个参数是一个逻辑。并且then的第二个参数实际咱们不多回调。都写catch来异常捕获的。code
其实写这篇文章的时候,对Promise仍是了解在表面上,但收获依然仍是有的,准建理解了Promise是如何实现的,好比resolve和then回调的关系,对象