本文已参与「新人创作礼」活动,一起开启掘金创作之路。
本文将实现一个符合Promise/A+规范的Promise类(目前只有then方法)
具体规范参考上篇文章:
你该知道的promise(1):A+规范中英文对照翻译
/**
* 规范2.1.
*/
const PENDING = 'PENDING';
const FULFILLED = 'FULFILLED';
const REJECTED = 'REJECTED';
class MyPromise {
constructor(fun) {
this.status = PENDING;
this.value = null;
this.reason = null;
this.resolveCallbacks = [];
this.rejectCallbacks = [];
try {
fun(this.resolve, this.reject)
} catch (e) {
this.reject(e)
}
}
resolve = (v) => {
// 2.1.1
if(this.status === PENDING) {
this.status = FULFILLED;
this.value = v;
// 按顺序调用resolveCallbacks
this.resolveCallbacks.forEach(cb => cb(v))
}
}
reject = (r) => {
// 2.1.1
if(this.status === PENDING) {
this.status = REJECTED;
this.reason = r;
// 按顺序调用rejectCallbacks
this.rejectCallbacks.forEach(cb => cb(r))
}
}
}
/**
* 规范2.2.
* 应该始终返回一个Promise对象确保可以使用then无限调下去
* 虽然resolve是同步执行的,我们必须保证then是异步调用的,
*
* @param {*} onFulfilled
* @param {*} onRejected
*/
MyPromise.prototype.then = function(onFulfilled, onRejected) {
let promise2;
// 2.2.1
onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : v => v;
onRejected = typeof onRejected == 'function' ? onRejected : r => {throw r};
//2.2.2
if(this.status == FULFILLED) {
return promise2 = new MyPromise((resolve, reject) => {
//2.2.4 我们用setTimeout来模拟异步调用(并不能实现微任务和宏任务的执行机制,只是保证异步调用)
setTimeout(() => {
try {
// 2.2.5 onFulfilled 和 onRejected 作为函数直接调用
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject)
} catch (e){
reject(e)
}
})
})
}
//2.2.3
if(this.status == REJECTED) {
return promise2 = new MyPromise((resolve, reject) => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject)
} catch (e){
reject(e)
}
})
})
}
if(this.status == PENDING) {
return promise2 = new MyPromise((resolve, reject) => {
// 将fulfilled回调暂存
this.resolveCallbacks.push((v) => {
setTimeout(() => {
try {
let x = onFulfilled(v);
resolvePromise(promise2, x, resolve, reject)
} catch (e){
reject(e)
}
})
})
// 将rejected回调暂存
this.rejectCallbacks.push((r) => {
setTimeout(() => {
try {
let x = onRejected(r);
resolvePromise(promise2, x, resolve, reject)
} catch (e){
reject(e)
}
})
})
})
}
}
/**
* 规范2.3.
* 如果x为Promise对象,则promise2接收x状态
* 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,
* 则优先采用首次调用并忽略剩下的调用
*
* @param {*} promise2 : new MyPromise()
* @param {*} x : new MyPromise()
* @param {*} resolve : promise2的resolve
* @param {*} reject : promise2的reject
*
*/
function resolvePromise(promise2, x, resolve, reject) {
// 2.3.1
if(promise2 == x) {
reject(new TypeError('error 🐯'))
}
//2.3.2
if(x instanceof MyPromise) {
x.then(y => {
resolvePromise(promise2, y, resolve, reject)
}, r => {
reject(r)
})
} else {
// 2.3.3
if(x && ['object', 'function'].includes(typeof x)) {
let used = false;
let then;
try {
then = x.then;
} catch (e) {
if(used) return;
used = true;
reject(e)
}
if(typeof then == 'function') {
try {
then.call(x, y => {
if(used) return;
used = true;
resolvePromise(promise2, y, resolve, reject)
}, r => {
if(used) return;
used = true;
reject(r)
})
} catch (e) {
if(used) return;
used = true;
reject(e);
}
} else {
if(used) return;
used = true;
resolve(x);
}
} else {
// 2.3.4
resolve(x);
}
}
}
// 使用promises-aplus-tests 测试时需要
// MyPromise.defer = MyPromise.deferred = function () {
// let dfd = {};
// dfd.promise = new MyPromise((resolve, reject) => {
// dfd.resolve = resolve;
// dfd.reject = reject;
// });
// return dfd;
// }
// module.exports = MyPromise;
有开源的测试工具通872个测试用专门来测试是否符合 Promise/A+ 规范
promises-aplus-tests;
可以通过一下命令安装
npm install -g promises-aplus-tests
然后在编写的js文件中加入如下代码:
MyPromise.defer = MyPromise.deferred = function () {
let dfd = {};
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = MyPromise;
执行:
promises-aplus-tests promise.js