Promise是ES6新增的特性,已经被正式列入ES6的规范中。Promise是一个对象,代表了一个异步操作的失败或成功。应该先熟悉Promise的使用再学习Promise的实现。
//实现Promise
//Promise的实现需要考虑一下几点
/*1.then 支持链式调用,返回一个新的Promise
2.处理异步问题,先用onResolvedCallbacks和onRejectedCallbacks分别把成功和失败的回调存起来
3.为了让链式回调正常进行下去,需要判断onFulfilled和onRejected的类型
4.onFulfilled和onRejected需要被异步调用,这里用setTimeout模拟异步
5.处理Promise的resolve
*/
//各个状态常量
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class Promise {
constructor(executor) {
this.status = PENDING;
this.value = undefined;
this.reason = undefined;
//用来存放成功和失败的回调
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) {
//使用setTimeout模拟异步
setTimeout(() => {
try {
let x = onFulfilled(this.value);
// x可能是一个proimise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
//异步操作失败
if (this.status === REJECTED) {
//使用
setTimeout(() => {
try {
let x = onRejected(this.reason);
// x可能是一个proimise
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
}
if (this.status === PENDING) {
this.onResolvedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
}, 0);
});
}
});
return promise2;
}
}
const resolvePromise = (promise2, x, resolve, reject) => {
// 自己等待自己完成是错误的实现,用一个类型错误,结束掉 promise Promise/A+ 2.3.1
if (promise2 === x) {
return reject(
new TypeError("Chaining cycle detected for promise #<Promise>"));
}
// Promise/A+ 2.3.3.3.3 只能调用一次
let called;
// 后续的条件要严格判断 保证代码能和别的库一起使用
if ((typeof x === "object" && x != null) || typeof x === "function") {
try {
// 为了判断 resolve 过的就不用再 reject 了(比如 reject 和 resolve 同时调用的时候) Promise/A+ 2.3.3.1
let then = x.then;
if (typeof then === "function") {
// 不要写成 x.then,直接 then.call 就可以了 因为 x.then 会再次取值,Object.defineProperty Promise/A+ 2.3.3.3
then.call(
x, (y) => {
// 根据 promise 的状态决定是成功还是失败
if (called) return;
called = true;
// 递归解析的过程(因为可能 promise 中还有 promise) Promise/A+ 2.3.3.3.1
resolvePromise(promise2, y, resolve, reject);
}, (r) => {
// 只要失败就失败 Promise/A+ 2.3.3.3.2
if (called) return;
called = true;
reject(r);
});
} else {
// 如果 x.then 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.3.4
resolve(x);
}
} catch (e) {
// Promise/A+ 2.3.3.2
if (called) return;
called = true;
reject(e);
}
} else {
// 如果 x 是个普通值就直接返回 resolve 作为结果 Promise/A+ 2.3.4
resolve(x);
}
};
上面就是实现Promise的完整代码,下面通过promises-aplus-tests包进行测试是否符合Promise A+规范。测试前需要加上以下代码
//上面是实现代码
Promise.defer = Promise.deferred = function () {
let dfd = {};
dfd.promise = new Promise((resolve,reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
好了,全局安装包然后验证
全局安装命令:npm i promises-aplus-tests -g
验证命令:promises-aplus-tests promise.js
promise.js是实现Promise代码所在文件
最终测试结果:
通过872个用例
总结:Promise对我来说还是挺难的,更多是把它背下来了,但是每写一次,理解都会多一点。路漫漫其修远兮,吾将上下而求索~