讲promise文章很多,本文主要是学习手写promise源码记录,实测跑过了所有PromiseA+用例,希望对大家有所帮助。
代码即文档
// 先定义三个常量表示状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
class MyPromise {
constructor(executor){
this.status = PENDING; // 储存状态的变量,初始值是 pending
this.value = null; // 成功之后的值
this.reason = null; // 失败之后的原因
this.onFulfilledCallbacks = []; // 存储成功回调函数
this.onRejectedCallbacks = []; // 存储失败回调函数
// resolve和reject为什么要用箭头函数?
// 如果直接调用的话,普通函数this指向的是window或者undefined
// 用箭头函数就可以让this指向当前实例对象
// 更改成功后的状态
let resolve = (value) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
this.status = FULFILLED; // 状态修改为成功
this.value = value; // 保存成功之后的值
// resolve里面将所有成功的回调拿出来执行
this.onFulfilledCallbacks.forEach(fn => fn());
}
}
// 更改失败后的状态
let reject = (reason) => {
// 只有状态是等待,才执行状态修改
if (this.status === PENDING) {
this.status = REJECTED; // 状态成功为失败
this.reason = reason; // 保存失败后的原因
// reject里面将所有失败的回调拿出来执行
this.onRejectedCallbacks.forEach(fn => fn());
}
}
// executor 是一个执行器,进入会立即执行
// 并传入resolve和reject方法
try {
executor(resolve, reject)
} catch (error) {
this.reject(error)
}
}
then(onFulfilled, onRejected) {
// 如果不传,就使用默认函数
const realOnFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
const realOnRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason};
let promise2 = new MyPromise((resolve, reject) => {
const fulfilledMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 获取成功回调函数的执行结果
const x = realOnFulfilled(this.value);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
}
const rejectedMicrotask = () => {
// 创建一个微任务等待 promise2 完成初始化
queueMicrotask(() => {
try {
// 调用失败回调,并且把原因返回
const x = realOnRejected(this.reason);
// 传入 resolvePromise 集中处理
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error)
}
})
}
// 判断状态
if (this.status === FULFILLED) {
fulfilledMicrotask()
} else if (this.status === REJECTED) {
rejectedMicrotask()
} else if (this.status === PENDING) {
// 等待
// 因为不知道后面状态的变化情况,所以将成功回调和失败回调存储起来
// 等到执行成功失败函数的时候再传递
this.onFulfilledCallbacks.push(fulfilledMicrotask);
this.onRejectedCallbacks.push(rejectedMicrotask);
}
})
return promise2;
}
catch (onRejected) {
// 只需要进行错误处理
this.then(undefined, onRejected);
}
finally (fn) {
return this.then((value) => {
return MyPromise.resolve(fn()).then(() => {
return value;
});
}, (error) => {
return MyPromise.resolve(fn()).then(() => {
throw error
});
});
}
static resolve(parameter) {
// 如果传入是promise,则直接返回
if (parameter instanceof MyPromise) {
return parameter;
}
return new MyPromise((resolve) => {
resolve(parameter)
})
}
static reject(parameter) {
if (parameter instanceof MyPromise) {
return parameter;
}
return new Promise((resolve, reject) => {
reject(parameter)
})
}
static all(promiseList) {
return new MyPromise((resolve, reject) => {
const result = [];
const length = promiseList.length;
let count = 0;
if (length === 0) {
return resolve(result);
}
promiseList.forEach((promise, index) => {
MyPromise.resolve(promise).then((value) => {
count++;
result[index] = value;
if (count === length) {
resolve(result);
}
}, (reason) => {
reject(reason);
})
})
})
}
static allSettled(promiseList) {
return new MyPromise((resolve, reject) => {
const result = [];
const length = promiseList.length;
const count = 0;
if (length === 0) {
return resolve();
}
promiseList.forEach((promise, index) => {
MyPromise.resolve(promise).then((value) => {
count++;
result[index] = {
status: FULFILLED,
value: value
}
if (count === length) {
return resolve(result);
}
}, (reason) => {
count++;
result[index] = {
status: REJECTED,
reason: reason
}
if (count === length) {
return resolve(result);
}
})
})
})
}
static race(promiseList) {
return new MyPromise((resolve, reject) => {
const length = promiseList.length;
if (length === 0) {
return resolve();
}
for (let i = 0; i < length; i++) {
MyPromise.resolve(promiseList[i]).then(value => {
return resolve(value);
}, (reason) => {
return reject(reason);
});
}
})
}
}
function resolvePromise(promise, x, resolve, reject) {
// 如果 promise 和 x 指向同一对象,以 TypeError 为据因拒绝执行 promise
// 这是为了防止死循环
if (promise === x) {
return reject(new TypeError('The promise and the return value are the same'));
}
if (typeof x === 'object' || typeof x === 'function') {
// 这个坑是跑测试的时候发现的,如果x是null,应该直接resolve
if (x === null) {
return resolve(x);
}
let then;
try {
// 把 x.then 赋值给 then
then = x.then;
} catch (error) {
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
return reject(error);
}
// 如果 then 是函数
if (typeof then === 'function') {
let called = false;
// 将 x 作为函数的作用域 this 调用之
// 传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
// 名字重名了,我直接用匿名函数了
try {
then.call(
x,
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
y => {
// 如果 resolvePromise 和 rejectPromise 均被调用,
// 或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 实现这条需要前面加一个变量called
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
// 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
r => {
if (called) return;
called = true;
reject(r);
});
} catch (error) {
// 如果调用 then 方法抛出了异常 e:
// 如果 resolvePromise 或 rejectPromise 已经被调用,则忽略之
if (called) return;
// 否则以 e 为据因拒绝 promise
reject(error);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
};
MyPromise.deferred = function () {
var result = {};
result.promise = new MyPromise(function (resolve, reject) {
result.resolve = resolve;
result.reject = reject;
});
return result;
}
module.exports = MyPromise;
遇到的问题
在实现then方法时 ,为了实现promise链式调用,对then方法结果返回进行promise封装,代码如下:
但是在执行时报如下错误:
(node:10540) UnhandledPromiseRejectionWarning: ReferenceError: Cannot access 'promise2' before initialization
解决方法:从错误提示可以看出,我们必须等待 promise2完成初始化,再此我们可以通过setTimeout实现异步,但是promise A+规范中使用微任务实现,所以采用queueMicrotask