手写 Promise/A+ 规范
手写 Promise/A+ 规范
所谓 Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
想要写出 Promise/A+ 规范的 promsie,首先要熟读 Promise/A+ 的英文规范文档,建议先看几遍英文文档,了解一下规则。还有这篇中文文档翻译的也很到位 中文规范文档
状态:
Promise存在三个状态(state)pending、fulfilled、rejected
pending(等待态)为初始态,并可以转化为 fulfilled(成功态)和 rejected(失败态),一旦确定就不可以改变
如果 executor 函数报错,则直接执行 reject(reason);
then 方法:
promise的 then
方法接收两个可选参数,表示该 promise
状态改变时的回调
then
方法返回一个promise
,then
方法可以被同一个 promise 调用多次
promise2 = promise1.then(onFulfilled,onRejected)
resolvePromise 方法:
这个方法是用来处理 then 方法中的 onFulfilled
和 onRejected
这两个参数,当这两个参数是函数的时候,处理它们的返回值
下面就根据日常对 promise 的使用规则出发去写一下 Promise/A+ 规范吧。
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
// 同一个 promsie 实例可能会被多次调用
this.onResolvedCallbacks = [];
this.onRejectedCallbacks = [];
let resolve = value => {
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
this.onResolvedCallbacks.forEach(fn => fn());
}
}
let reject = reason => {
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
this.onRejectedCallbacks.forEach(fn => fn());
}
}
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 解决 onFufilled,onRejected 没有传值的问题
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : v => v;
// 因为错误的值要让后面访问到,所以这里也要抛出错误,不然会在之后 then 的 resolve 中捕获
onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
// 每次调用 then 都返回一个新的 promise
let promise2 = new Promise((resolve, reject) => {
if (this.status === FULFILLED) {
//Promise/A+ 2.2.4 --- setTimeout
// 在执行上下文栈中只包含平台代码之前,onFulfilled或onRejected一定不能被调用 [3.1]
setTimeout(() => {
try {
let x = onFulfilled(this.value);
// x可能是一个proimise
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === REJECTED) {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
}, 0);
})
}
})
// Promise/A+ 2.2.7. then必须返回一个promise [3.3]
return promise2;
}
}
const resolvePromise = function (promise2, x, resolve, reject) {
// 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise Promise/A+ 2.3.1
if (promise2 === x) {
return reject(new TypeError('err'));
}
// Promise/A+ 2.3.3.3.3 只能调用一次
let called;
if ((x && typeof x === 'object') || typeof x === 'function') {
try {
let then = x.then;
if (typeof then === 'function') {
// 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,防止 Object.defineProperty 中设置的 get 只能访问一次的情况 Promise/A+ 2.3.3.3
then.call(x,
value => {
// 根据 promise 的状态决定是成功还是失败
if (called) return;
called = true;
// 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, value, resolve, reject);
},
err => {
// 只要失败就失败 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(err);
}
)
} else {
// 如果 x.then 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.3.4
resolve(x);
}
} catch (error) {
// Promise/A+ 2.3.3.2
if (called) return;
called = true;
reject(error);
}
} else {
// 如果 x 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.4
resolve(x);
}
}
Promise 写完之后可以通过 promises-aplus-tests 这个包对我们写的代码进行测试,看是否符合 A+ 规范。不过测试前还得加一段代码:
// promise.js
// 这里是上面写的 Promise 全部代码
Promise.defer = Promise.deferred = function () {
let dfd = {}
dfd.promise = new Promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
module.exports = Promise;
全局安装:
npm i promises-aplus-tests -g
终端下执行验证命令:
promises-aplus-tests promise.js
结语:刚开始接触 promise 的时候,觉得这东西好抽象,但是用了段时间之后,感觉确实好用。作为一名有追求的程序员,怎么能不挑战一下 promise/A+ 呢!不能被它的规范很多吓到,其实它的规范也是我们平时使用的规则,从这个点出发去写,就不那么枯燥了嘿嘿