拥有三种状态:git
pendinggithub
fulfillednpm
rejected数组
(1) promise 对象初始化状态为 pendingpromise
(2) 当调用resolve(成功),会由pending => fulfilled框架
(3) 当调用reject(失败),会由pending => rejected异步
标准中只有一个then方法,没有catch,race,all等方法,甚至没有构造函数 Promise标准中仅指定了Promise对象的then方法的行为,其它一切咱们常见的方法/函数都并无指定,包括catch,race,all等经常使用方法,甚至也没有指定该如何构造出一个Promise对象,另外then也没有通常实现中(Q, $q等)所支持的第三个参数,通常称onProgressasync
then方法返回一个新的Promise Promise的then方法返回一个新的Promise,而不是返回this,此处在下文会有更多解释函数
promise2 = promise1.then(alert)
promise2 != promise1 // true
复制代码
不一样Promise的实现须要能够相互调用(interoperable)
Promise的初始状态为pending,它能够由此状态转换为fulfilled或者rejected,一旦状态肯定,就不能够再次转换为其它状态,状态肯定的过程称为settle
除了上文中说的then方法,本文实现的Promise会在基于标准之上,新增catch,race,all,resolve,reject方法
Promise.prototype.catch,在链式写法中能够捕获前面的异常
promise.catch(onRejected)
// 至关于
promise.then(null, onRrejected);
// 注意
// onRejected 不能捕获当前onFulfilled中的异常
promise.then(onFulfilled, onRrejected);
// 能够写成:
promise.then(onFulfilled)
.catch(onRrejected);
复制代码
Promise.resolve,返回一个fulfilled状态的promise对象
Promise.resolve('hello').then(function(value){
console.log(value);
});
Promise.resolve('hello');
// 至关于
const promise = new Promise(resolve => {
resolve('hello');
});
复制代码
Promise.reject,返回一个rejected状态的promise对象
Promise.reject(24);
new Promise((resolve, reject) => {
reject(24);
});
复制代码
Promise.all,接收一个promise对象数组为参数 只有所有 promise 进入 fulfilled 状态才会resolve 一般会用来处理 多个并行异步操做
const p1 = new Promise((resolve, reject) => {
resolve(1);
});
const p2 = new Promise((resolve, reject) => {
resolve(2);
});
const p3 = new Promise((resolve, reject) => {
resolve(3);
});
Promise.all([p1, p2, p3]).then(data => {
console.log(data); // [1, 2, 3] 结果顺序和promise实例数组顺序是一致的
}, err => {
console.log(err);
});
复制代码
Promise.race,接收一个promise对象数组为参数 Promise.race 只要有一个promise对象进入 fulfilled 或者 rejected 状态的话,就会继续进行后面的处理
function timerPromisefy(delay) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
resolve(delay);
}, delay);
});
}
var startDate = Date.now();
Promise.race([
timerPromisefy(10),
timerPromisefy(20),
timerPromisefy(30)
]).then(function (values) {
console.log(values); // 10
});
复制代码
下面咱们就来一步一步实现一个Promise
由于标准并无指定如何构造一个Promise对象,因此咱们一样以目前通常Promise实现中通用的方法来构造一个Promise对象,也是ES6原生Promise里所使用的方式,即:
// Promise构造函数接收一个executor函数,executor函数执行完同步或异步操做后,调用它的两个参数resolve和reject
var promise = new Promise(function(resolve, reject) {
/* 若是操做成功,调用resolve并传入value 若是操做失败,调用reject并传入reason */
})
复制代码
咱们先实现构造函数的框架以下:
function Promise(executor) {
var self = this
self.status = 'pending' // Promise当前的状态
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve时的回调函数集,由于在Promise结束以前有可能有多个回调添加到它上面
self.onRejectedCallback = [] // Promise reject时的回调函数集,由于在Promise结束以前有可能有多个回调添加到它上面
executor(resolve, reject) // 执行executor并传入相应的参数
}
复制代码
上面的代码基本实现了Promise构造函数的主体,但目前还有两个问题:
咱们给executor函数传了两个参数:resolve和reject,这两个参数目前尚未定义
executor有可能会出错(throw),相似下面这样,而若是executor出错,Promise应该被其throw出的值reject:
new Promise(function(resolve, reject) {
throw 2
})
复制代码
因此咱们须要在构造函数里定义resolve和reject这两个函数:
function Promise(executor) {
var self = this
self.status = 'pending' // Promise当前的状态
self.data = undefined // Promise的值
self.onResolvedCallback = [] // Promise resolve时的回调函数集,由于在Promise结束以前有可能有多个回调添加到它上面
self.onRejectedCallback = [] // Promise reject时的回调函数集,由于在Promise结束以前有可能有多个回调添加到它上面
function resolve(value) {
// TODO
}
function reject(reason) {
// TODO
}
try { // 考虑到执行executor的过程当中有可能出错,因此咱们用try/catch块给包起来,而且在出错后以catch到的值reject掉这个Promise
executor(resolve, reject) // 执行executor
} catch(e) {
reject(e)
}
}
复制代码
接下来,咱们实现resolve和reject这两个函数
function Promise(executor) {
// ...
function resolve(value) {
if (self.status === 'pending') {
self.status = 'fulfilled'
self.data = value
//执行resolve的回调函数,将value传递到callback中
self.onResolvedCallback.forEach(callback => callback(value))
}
}
function reject(reason) {
if (self.status === 'pending') {
self.status = 'rejected'
self.data = reason
//执行reject的回调函数,将reason传递到callback中
self.onRejectedCallback.forEach(callback => callback(reason))
}
}
// ...
}
复制代码
基本上就是在判断状态为pending以后把状态改成相应的值,并把对应的value和reason存在self的data属性上面,以后执行相应的回调函数,逻辑很简单,这里就很少解释了。
Promise对象有一个then方法,用来注册在这个Promise状态肯定后的回调,很明显,then方法须要写在原型链上。then方法会返回一个Promise,关于这一点,Promise/A+标准并无要求返回的这个Promise是一个新的对象,但在Promise/A标准中,明确规定了then要返回一个新的对象,目前的Promise实现中then几乎都是返回一个新的Promise对象,因此在咱们的实现中,也让then返回一个新的Promise对象。
关于这一点,我认为标准中是有一点矛盾的:
标准中说,若是promise2 = promise1.then(onResolved, onRejected)里的onResolved/onRejected返回一个Promise,则promise2直接取这个Promise的状态和值为己用,但考虑以下代码:
promise2 = promise1.then(function foo(value) {
return Promise.reject(3)
})
复制代码
此处若是foo运行了,则promise1的状态必然已经肯定且为resolved,若是then返回了this(即promise2 === promise1),说明promise2和promise1是同一个对象,而此时promise1/2的状态已经肯定,没有办法再取Promise.reject(3)的状态和结果为己用,由于Promise的状态肯定后就不可再转换为其它状态。
另外每一个Promise对象均可以在其上屡次调用then方法,而每次调用then返回的Promise的状态取决于那一次调用then时传入参数的返回值,因此then不能返回this,由于then每次返回的Promise的结果都有可能不一样。
下面咱们来实现then方法:
// then方法接收两个参数,onResolved,onRejected,分别为Promise成功或失败后的回调
Promise.prototype.then = function (onResolve, onReject) {
let self = this
let promise2
// 根据标准,若是then的参数不是function,则咱们须要忽略它,此处以以下方式处理
onResolve = typeof onResolve==='function' ? onResolve : function(value){}
onReject = typeof onReject==='function' ? onReject : function(reason){}
if (self.status === 'pending') {
return promise2 = new Promise(function(resolve, reject){
})
}
if (self.status === 'fulfilled') {
return promise2 = new Promise(function(resolve, reject){
})
}
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject){
})
}
}
复制代码
Promise总共有三种可能的状态,咱们分三个if块来处理,在里面分别都返回一个new Promise。
根据标准,咱们知道,对于以下代码,promise2的值取决于then里面函数的返回值:
promise2 = promise1.then(function(value) {
return 4
}, function(reason) {
throw new Error('sth went wrong')
})
复制代码
若是promise1被resolve了,promise2的将被4 resolve,若是promise1被reject了,promise2将被new Error('sth went wrong') reject,更多复杂的状况再也不详述。
因此,咱们须要在then里面执行onResolved或者onRejected,并根据返回值(标准中记为x)来肯定promise2的结果,而且,若是onResolved/onRejected返回的是一个Promise,promise2将直接取这个Promise的结果:
// then方法接收两个参数,onResolved,onRejected,分别为Promise成功或失败后的回调
Promise.prototype.then = function (onResolve, onReject) {
let self = this
let promise2
// 根据标准,若是then的参数不是function,则咱们须要忽略它,此处以以下方式处理
onResolve = typeof onResolve==='function' ? onResolve : function(value){}
onReject = typeof onReject==='function' ? onReject : function(reason){}
if (self.status === 'fulfilled') {
// 若是promise1(此处即为this/self)的状态已经肯定而且是fulfilled,咱们调用onResolved
// 由于考虑到有可能throw,因此咱们将其包在try/catch块里
return promise2 = new Promise(function(resolve, reject){
try {
var x = onResolved(self.data)
if (x instanceof Promise) { // 若是onResolved的返回值是一个Promise对象,直接取它的结果作为promise2的结果
x.then(resolve, reject)
} else {
resolve(x) // 不然,以它的返回值作为promise2的结果
}
} catch (e) {
reject(e) // 若是出错,以捕获到的错误作为promise2的结果
}
})
}
// 此处与前一个if块的逻辑几乎相同,区别在于所调用的是onRejected函数,就再也不作过多解释
if (self.status === 'rejected') {
return promise2 = new Promise(function(resolve, reject){
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (e) {
reject(e)
}
})
}
if (self.status === 'pending') {
// 若是当前的Promise还处于pending状态,咱们并不能肯定调用onResolved仍是onRejected,
// 只能等到Promise的状态肯定后,才能确实如何处理。
// 因此咱们须要把咱们的 两种状况 的处理逻辑作为callback放入promise1(此处即this/self)的回调数组里
// 逻辑自己跟第一个if块内的几乎一致,此处不作过多解释
return promise2 = new Promise(function(resolve, reject){
self.onResolvedCallback.push(function(value) {
try {
var x = onResolved(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (e) {
reject(e)
}
})
self.onRejectedCallback.push(function(reason) {
try {
var x = onRejected(self.data)
if (x instanceof Promise) {
x.then(resolve, reject)
} else {
resolve(x)
}
} catch (e) {
reject(e)
}
})
})
}
}
// 为了下文方便,咱们顺便实现一个catch方法
Promise.prototype.catch = function(onRejected) {
return this.then(null, onRejected)
}
复制代码
至此,咱们基本实现了Promise标准中所涉及到的内容,但还有几个问题:
// 此处用MyPromise来表明咱们实现的Promise
new MyPromise(function(resolve, reject) { // 咱们实现的Promise
setTimeout(function() {
resolve(42)
}, 2000)
}).then(function() {
return new Promise.reject(2) // ES6的Promise
}).then(function() {
return Q.all([ // Q的Promise
new MyPromise(resolve=>resolve(8)), // 咱们实现的Promise
new Promise.resolve(9), // ES6的Promise
Q.resolve(9) // Q的Promise
])
})
复制代码
咱们前面实现的代码并无处理这样的逻辑,咱们只判断了onResolved/onRejected的返回值是否为咱们实现的Promise的实例,并无作任何其它的判断,因此上面这样的代码目前是没有办法在咱们的Promise里正确运行的。new Promise(resolve=>resolve(8))
.then()
.then()
.then(function foo(value) {
alert(value)
})
复制代码
正确的行为应该是alert出8,而若是拿咱们的Promise,运行上述代码,将会alert出undefined。这种行为称为穿透,即8这个值会穿透两个then(说Promise更为准确)到达最后一个then里的foo函数里,成为它的实参,最终将会alert出8。经过观察,会发现咱们但愿下面这段代码
new Promise(resolve=>resolve(8))
.then()
.catch()
.then(function(value) {
alert(value)
})
复制代码
跟下面这段代码的行为是同样的
new Promise(resolve=>resolve(8))
.then(function(value){
return value
})
.catch(function(reason){
throw reason
})
.then(function(value) {
alert(value)
})
复制代码
因此若是想要把then的实参留空且让值能够穿透到后面,意味着then的两个参数的默认值分别为function(value) {return value},function(reason) {throw reason}
。
因此咱们只须要把then里判断onResolved和onRejected的部分改为以下便可:
onResolved = typeof onResolved === 'function' ? onResolved : function(value) {return value}
onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {throw reason}
复制代码
关于不一样Promise间的交互,其实标准里是有说明的,其中详细指定了如何经过then的实参返回的值来决定promise2的状态,咱们只须要按照标准把标准的内容转成代码便可。
这里简单解释一下标准:
即咱们要把onResolved/onRejected的返回值,x,当成一个多是Promise的对象,也即标准里所说的thenable,并以最保险的方式调用x上的then方法,若是你们都按照标准实现,那么不一样的Promise之间就能够交互了。而标准为了保险起见,即便x返回了一个带有then属性但并不遵循Promise标准的对象(好比说这个x把它then里的两个参数都调用了,同步或者异步调用(PS,原则上then的两个参数须要异步调用,下文会讲到),或者是出错后又调用了它们,或者then根本不是一个函数),也能尽量正确处理。
关于为什么须要不一样的Promise实现可以相互交互,我想缘由应该是显然的,Promise并非JS一早就有的标准,不一样第三方的实现之间是并不相互知晓的,若是你使用的某一个库中封装了一个Promise实现,想象一下若是它不能跟你本身使用的Promise实现交互的场景。。。
建议各位对照着标准阅读如下代码,由于标准对此说明的很是详细,因此你应该可以在任意一个Promise实现中找到相似的代码:
/* resolvePromise函数即为根据x的值来决定promise2的状态的函数 也即标准中的[Promise Resolution Procedure](https://promisesaplus.com/#point-47) x为`promise2 = promise1.then(onResolved, onRejected)`里`onResolved/onRejected`的返回值 `resolve`和`reject`其实是`promise2`的`executor`的两个实参,由于很难挂在其它的地方,因此一并传进来。 相信各位必定能够对照标准把标准转换成代码,这里就只标出代码在标准中对应的位置,只在必要的地方作一些解释 */
function resolvePromise (promise2, x, resolve, reject) {
let then
let thenCalledOrThrow = false
if (promise2 === x) { // 对应标准2.3.1节
// 这里可能有童鞋会问,何时会触发promise2 === x这个条件,首先咱们对标准2.3.1节中的promise2===x在作下解释
// 条件promise === x ,至关于promise.then以后return了本身,由于then会等待return后的promise,致使本身等待本身,一直处于等待。
/** 好比: let p1 = new Promise(function(resolve, reject) { setTimeout(() => {reject(1.1)}, 1000) }) let p1then = p1.then(function (value) { return p1then }, function (err) { return p1then }) p1then.then(value => { console.log('value:', value) }, err => { console.log('err:', err) }) 这段代码就会触发promise2 === x的判断,咱们为了promise不一直处于等待状态,根据标准规范, 这里咱们须要抛出'Chaining cycle detected for promise',即‘循环引用’的错误信息 */
reject(new TypeError('Chaining cycle detected for promise!'))
return
}
if (x instanceof Promise) { // 对应标准2.3.2节
// 若是x的状态尚未肯定,那么它是有可能被一个thenable决定最终状态和值的
// 因此这里须要作一下处理,而不能一律的觉得它会被一个“正常”的值resolve
if (x.status === 'pending') {
x.then(value => {
resolvePromise(promise2, value, resolve, reject)
}, err => {
reject(err)
})
} else { // 但若是这个Promise的状态已经肯定了,那么它确定有一个“正常”的值,而不是一个thenable,因此这里直接取它的状态
x.then(resolve, reject)
}
return
}
if ((x !== null) && ((typeof x === 'function') || (typeof x === 'object'))) {
try {
// 2.3.3.1 由于x.then有多是一个getter,这种状况下屡次读取就有可能产生反作用
// 即要判断它的类型,又要调用它,这就是两次读取
then = x.then //because x.then could be a getter
if (typeof then === 'function') { // 2.3.3.3
then.call(x, value => { // 2.3.3.3.1
if (thenCalledOrThrow) return // 2.3.3.3.3 即这三处谁选执行就以谁的结果为准
thenCalledOrThrow = true
resolvePromise(promise2, value, resolve, reject) // 2.3.3.3.1
return
}, err => { // 2.3.3.3.2
if (thenCalledOrThrow) return // 2.3.3.3.3 即这三处谁选执行就以谁的结果为准
thenCalledOrThrow = true
reject(err)
return
})
} else { // 2.3.3.4
resolve(x)
}
} catch (e) { // 2.3.3.2
if (thenCalledOrThrow) return // 2.3.3.3.3 即这三处谁选执行就以谁的结果为准
thenCalledOrThrow = true
reject(e)
return
}
} else { // 2.3.4
resolve(x)
}
}
复制代码
而后咱们使用这个函数的调用替换then里几处判断x是否为Promise对象的位置便可,见下方完整代码。
最后,咱们刚刚说到,原则上,promise.then(onResolved, onRejected)里的这两个函数须要异步调用,关于这一点,标准里也有说明:
In practice, this requirement ensures that onFulfilled and onRejected execute asynchronously, after the event loop turn in which then is called, and with a fresh stack.
因此咱们须要对咱们的代码作一点变更,即在四个地方加上setTimeout(fn, 0)
Tip: 咱们这里增长setTimeout,涉及到js执行栈以及js单线程和eventLoop相关的知识,各位对js的执行栈、js单线程、eventLoop不太了解的,能够谷歌查阅下相关资料,后边我有空也会写一篇js执行栈、js的单线程、eventloop的讲解文章。下面的代码中,我也会简单写一些加入setTimeout的缘由分析。
function Promise (executor) {
let self = this
self.status = 'pending' //Promise当前状态
self.data = undefined //当前Promise的值
self.onResolvedCallback = [] //Promise resolve时的回调函数集合
self.onRejectedCallback = [] //Promise reject时的回调函数集合
function resolve (value) { // value成功态时接收的终值
if (value instanceof Promise) {
value.then(resolve, reject)
return
}
// 为何resolve 加setTimeout?
// 2.2.4规范 onFulfilled 和 onRejected 只容许在 execution context 栈仅包含平台代码时运行.
// 注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环以后的新执行栈中执行。
setTimeout(function(){
// 调用resolve 回调对应onFulfilled函数
if (self.status === 'pending') {
// 只能由pending状态 => fulfilled状态 (避免调用屡次resolve reject)
self.status = 'fulfilled'
self.data = value
//执行resolve的回调函数,将value传递到callback中
self.onResolvedCallback.forEach(callback => callback(value))
}
})
}
function reject (reason) { // reason失败态时接收的拒因
setTimeout(function(){
// 调用reject 回调对应onRejected函数
if (self.status === 'pending') {
// 只能由pending状态 => rejected状态 (避免调用屡次resolve reject)
self.status = 'rejected'
self.data = reason
//执行reject的回调函数,将reason传递到callback中
self.onRejectedCallback.forEach(callback => callback(reason))
}
})
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
/** * [注册fulfilled状态/rejected状态对应的回调函数] * @param {function} onFulfilled fulfilled状态时 执行的函数 * @param {function} onRejected rejected状态时 执行的函数 * @return {function} newPromsie 返回一个新的promise对象 */
Promise.prototype.then = function (onResolve, onReject) {
let self = this
let promise2
// 处理参数默认值 保证参数后续可以继续执行
onResolve = typeof onResolve==='function' ? onResolve : function(value){return value}
onReject = typeof onReject==='function' ? onReject : function(reason){throw reason}
if (self.status === 'pending') { // 等待态
return promise2 = new Promise(function(resolve, reject){
// 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
self.onResolvedCallback.push(function(value){
try {
let x = onResolve(value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
self.onRejectedCallback.push(function(reason){
try {
let x = onReject(reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
// then里面的FULFILLED/REJECTED状态时 为何要加setTimeout ?
// 缘由:
// 其一 2.2.4规范 要确保 onFulfilled 和 onRejected 方法异步执行(且应该在 then 方法被调用的那一轮事件循环以后的新执行栈中执行) 因此要在resolve里加上setTimeout
// 其二 2.2.6规范 对于一个promise,它的then方法能够调用屡次.(当在其余程序中屡次调用同一个promise的then时 因为以前状态已经为FULFILLED/REJECTED状态,则会走的下面逻辑),因此要确保为FULFILLED/REJECTED状态后 也要异步执行onFulfilled/onRejected
// 其二 2.2.6规范 也是resolve函数里加setTimeout的缘由
// 总之都是 让then方法异步执行 也就是确保onFulfilled/onRejected异步执行
// 以下面这种情景 屡次调用p1.then
// p1.then((value) => { // 此时p1.status 由pending状态 => fulfilled状态
// console.log(value); // resolve
// // console.log(p1.status); // fulfilled
// p1.then(value => { // 再次p1.then 这时已经为fulfilled状态 走的是fulfilled状态判断里的逻辑 因此咱们也要确保判断里面onFuilled异步执行
// console.log(value); // 'resolve'
// });
// console.log('当前执行栈中同步代码');
// })
// console.log('全局执行栈中同步代码');
//
if (self.status === 'fulfilled') { // 成功态
return promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
try {
let x = onResolve(self.data)
resolvePromise(promise2, x, resolve, reject) // 新的promise resolve 上一个onFulfilled的返回值
} catch (e) {
reject(e) // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
},0)
})
}
if (self.status === 'rejected') { // 失败态
return promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
try {
let x = onReject(self.data)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
},0)
})
}
}
复制代码
功能是 在链式写法中能够捕获前面的异常
实现原理相似于then的第二个参数
好比这样:
// 用于promise方法链时 捕获前面onFulfilled/onRejected抛出的异常
Promise.prototype.catch = function (onReject) {
return this.then(null, onReject)
}
复制代码
返回一个fulfilled状态的promise对象
// 返回一个fulfilled状态的promise对象
Promise.resolve = function (value) {
return new Promise(function(resolve, reject){resolve(value)})
}
复制代码
返回一个rejected状态的promise对象
// 返回一个rejected状态的promise对象
Promise.reject = function (reason) {
return new Promise(function(resolve, reject){reject(reason)})
}
复制代码
接收一个promise对象数组为参数 只有所有 promise 进入 fulfilled 状态才会继续后面的处理 一般会用来处理 多个并行异步操做
/** * Promise.all Promise进行并行处理 * 参数: promise对象组成的数组做为参数 * 返回值: 返回一个Promise实例 * 当这个数组里的全部promise对象所有进入FulFilled状态的时候,才会resolve。 */
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let values = []
let count = 0
promises.forEach((promise, index) => {
promise.then(value => {
console.log('value:', value, 'index:', index)
values[index] = value
count++
if (count === promises.length) {
resolve(values)
}
}, reject)
})
})
}
复制代码
接收一个promise对象数组为参数 Promise.race 只要有一个promise对象进入 fulfilled 或者 rejected 状态的话,就会继续进行后面的处理
/** * Promise.race * 参数: 接收 promise对象组成的数组做为参数 * 返回值: 返回一个Promise实例 * 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理(取决于哪个更快) */
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, reject);
});
});
}
复制代码
function Promise (executor) {
let self = this
self.status = 'pending' //Promise当前状态
self.data = undefined //当前Promise的值
self.onResolvedCallback = [] //Promise resolve时的回调函数集合
self.onRejectedCallback = [] //Promise reject时的回调函数集合
function resolve (value) { // value成功态时接收的终值
if (value instanceof Promise) {
value.then(resolve, reject)
return
}
// 为何resolve 加setTimeout?
// 2.2.4规范 onFulfilled 和 onRejected 只容许在 execution context 栈仅包含平台代码时运行.
// 注1 这里的平台代码指的是引擎、环境以及 promise 的实施代码。实践中要确保 onFulfilled 和 onRejected 方法异步执行,且应该在 then 方法被调用的那一轮事件循环以后的新执行栈中执行。
setTimeout(function(){
// 调用resolve 回调对应onFulfilled函数
if (self.status === 'pending') {
// 只能由pending状态 => fulfilled状态 (避免调用屡次resolve reject)
self.status = 'fulfilled'
self.data = value
//执行resolve的回调函数,将value传递到callback中
self.onResolvedCallback.forEach(callback => callback(value))
}
})
}
function reject (reason) { // reason失败态时接收的拒因
setTimeout(function(){
// 调用reject 回调对应onRejected函数
if (self.status === 'pending') {
// 只能由pending状态 => rejected状态 (避免调用屡次resolve reject)
self.status = 'rejected'
self.data = reason
//执行reject的回调函数,将reason传递到callback中
self.onRejectedCallback.forEach(callback => callback(reason))
}
})
}
try {
executor(resolve, reject)
} catch (e) {
reject(e)
}
}
/** * resolve中的值几种状况: * 1.普通值 * 2.promise对象 * 3.thenable对象/函数 */
/** * 对resolve 进行改造加强 针对resolve中不一样值状况 进行处理 * @param {promise} promise2 promise1.then方法返回的新的promise对象 * @param {[type]} x promise1中onFulfilled的返回值 * @param {[type]} resolve promise2的resolve方法 * @param {[type]} reject promise2的reject方法 */
function resolvePromise (promise2, x, resolve, reject) {
let then
let thenCalledOrThrow = false // 避免屡次调用
if (promise2 === x) { // 若是从onFulfilled中返回的x 就是promise2 就会致使循环引用报错
reject(new TypeError('Chaining cycle detected for promise!'))
return
}
// 若是x是一个咱们本身写的promise对象
if (x instanceof Promise) {
if (x.status === 'pending') { // 若是为等待态需等待直至 x 被执行或拒绝 并解析value值
x.then(value => {
resolvePromise(promise2, value, resolve, reject)
}, err => {
reject(err)
})
} else { // 若是 x 已经处于执行态/拒绝态(值已经被解析为普通值),用相同的值执行传递下去 promise
x.then(resolve, reject)
}
return
}
// 若是 x 为对象或者函数
if ((x !== null) && ((typeof x === 'function') || (typeof x === 'object'))) {
try {
then = x.then //because x.then could be a getter
if (typeof then === 'function') { // 是不是thenable对象(具备then方法的对象/函数)
then.call(x, value => {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
resolvePromise(promise2, value, resolve, reject)
return
}, err => {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
reject(err)
return
})
} else { // 说明是一个普通对象/函数
resolve(x)
}
} catch (e) {
if (thenCalledOrThrow) return
thenCalledOrThrow = true
reject(e)
return
}
} else {
resolve(x)
}
}
/** * [注册fulfilled状态/rejected状态对应的回调函数] * @param {function} onFulfilled fulfilled状态时 执行的函数 * @param {function} onRejected rejected状态时 执行的函数 * @return {function} newPromsie 返回一个新的promise对象 */
Promise.prototype.then = function (onResolve, onReject) {
let self = this
let promise2
// 处理参数默认值 保证参数后续可以继续执行
onResolve = typeof onResolve==='function' ? onResolve : function(value){return value}
onReject = typeof onReject==='function' ? onReject : function(reason){throw reason}
if (self.status === 'pending') { // 等待态
return promise2 = new Promise(function(resolve, reject){
// 当异步调用resolve/rejected时 将onFulfilled/onRejected收集暂存到集合中
self.onResolvedCallback.push(function(value){
try {
let x = onResolve(value)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
self.onRejectedCallback.push(function(reason){
try {
let x = onReject(reason)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
})
})
}
// then里面的FULFILLED/REJECTED状态时 为何要加setTimeout ?
// 缘由:
// 其一 2.2.4规范 要确保 onFulfilled 和 onRejected 方法异步执行(且应该在 then 方法被调用的那一轮事件循环以后的新执行栈中执行) 因此要在resolve里加上setTimeout
// 其二 2.2.6规范 对于一个promise,它的then方法能够调用屡次.(当在其余程序中屡次调用同一个promise的then时 因为以前状态已经为FULFILLED/REJECTED状态,则会走的下面逻辑),因此要确保为FULFILLED/REJECTED状态后 也要异步执行onFulfilled/onRejected
// 其二 2.2.6规范 也是resolve函数里加setTimeout的缘由
// 总之都是 让then方法异步执行 也就是确保onFulfilled/onRejected异步执行
// 以下面这种情景 屡次调用p1.then
// p1.then((value) => { // 此时p1.status 由pending状态 => fulfilled状态
// console.log(value); // resolve
// // console.log(p1.status); // fulfilled
// p1.then(value => { // 再次p1.then 这时已经为fulfilled状态 走的是fulfilled状态判断里的逻辑 因此咱们也要确保判断里面onFuilled异步执行
// console.log(value); // 'resolve'
// });
// console.log('当前执行栈中同步代码');
// })
// console.log('全局执行栈中同步代码');
//
if (self.status === 'fulfilled') { // 成功态
return promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
try {
let x = onResolve(self.data)
resolvePromise(promise2, x, resolve, reject) // 新的promise resolve 上一个onFulfilled的返回值
} catch (e) {
reject(e) // 捕获前面onFulfilled中抛出的异常 then(onFulfilled, onRejected);
}
},0)
})
}
if (self.status === 'rejected') { // 失败态
return promise2 = new Promise(function(resolve, reject){
setTimeout(function(){
try {
let x = onReject(self.data)
resolvePromise(promise2, x, resolve, reject)
} catch (e) {
reject(e)
}
},0)
})
}
}
/** * Promise.all Promise进行并行处理 * 参数: promise对象组成的数组做为参数 * 返回值: 返回一个Promise实例 * 当这个数组里的全部promise对象所有进入FulFilled状态的时候,才会resolve。 */
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let values = []
let count = 0
promises.forEach((promise, index) => {
promise.then(value => {
console.log('value:', value, 'index:', index)
values[index] = value
count++
if (count === promises.length) {
resolve(values)
}
}, reject)
})
})
}
/** * Promise.race * 参数: 接收 promise对象组成的数组做为参数 * 返回值: 返回一个Promise实例 * 只要有一个promise对象进入 FulFilled 或者 Rejected 状态的话,就会继续进行后面的处理(取决于哪个更快) */
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
promises.forEach((promise) => {
promise.then(resolve, reject);
});
});
}
// 用于promise方法链时 捕获前面onFulfilled/onRejected抛出的异常
Promise.prototype.catch = function (onReject) {
return this.then(null, onReject)
}
// 返回一个fulfilled状态的promise对象
Promise.resolve = function (value) {
return new Promise(function(resolve, reject){resolve(value)})
}
// 返回一个rejected状态的promise对象
Promise.reject = function (reason) {
return new Promise(function(resolve, reject){reject(reason)})
}
Promise.deferred = Promise.defer = function() {
var defer = {}
defer.promise = new Promise(function(resolve, reject) {
defer.resolve = resolve
defer.reject = reject
})
return defer
}
try {
module.exports = Promise
} catch (e) {
}
// Promise核心内容完整测试方法
let promisesAplusTests = require("promises-aplus-tests")
promisesAplusTests(Promise, function(err){
console.log('err:', err);
//所有完成;输出在控制台中。或者检查`err`表示失败次数。
})
复制代码
Tip: 完整代码+测试demo文件地址
如何肯定咱们实现的Promise符合标准呢?Promise有一个配套的测试脚本,只须要咱们在一个CommonJS的模块中暴露一个deferred方法(即exports.deferred方法),就能够了,代码见上述代码的最后。而后执行以下代码便可执行测试:
npm i promises-aplus-tests
node ./Promise.js
复制代码