对于 Promise 其实本身并不陌生,可以说是经常使用了,面试题也做了不少,但是对于手动实现一个符合 promise A+规范,我还真的从来没有做过,特此花半天时间好好研究研究。
Promise A+
先来看看promise A+规范。我这仅过了四级的英语水平也是坑坑洼洼的看完了,建议还是都看一下,毕竟正统哈哈。
我简单做了个总结,有错误的话望指正:
- state
比较简单,就是有三种状态:
- pending 可以转为下面两种状态
- fulfilled 不可改变状态,必须有一个不能改变的 value
- rejected 不可改变状态,必须有一个不能改变的 reason
- then
promise.then(onFulfilled, onRejected)
;
- 2.1 onFulfilled,onRejected 都是可选的,如果它们不是函数,就必须被忽略。
- 2.2 onFulfilled,onRejected 为函数,必须在 promise 转为对应的状态后再传入对应的参数 value/reason 进行调用, 且只能被调用一次
- 2.3 onFulfilled,onRejected 都是异步执行的
- 2.4 then 可以被同一个 promise 调用多次
- 2.5 then 必须返回一个 promise,如果 onFulfilled, onRejected 返回了值 x ,就执行下方的 promise resolve produre
- Promise resolve procedure
[[Resolve]](promise, x)
-
3.1 promise 和 x 是同一个对象,抛出 TypeError 错误
-
3.2 x 是一个不为 null 的对象或者函数,先让 then 指向 x.then;x.then 不存在抛错 e,用 e 来 reject promise。
- 3.2.1 then 为函数,调用 then 并使用 x 作为 this,第一个参数 , 第二个参数 rejectPromise; resolvePromise 的结果 y 传入[[Resolve]](promise, y), 如此递归,rejectPromise 的结果 r, 用 r 拒绝 promise
- 3.2.2 then 不为函数,用 x 来 fulfill promise
-
3.3 x 不是一个对象或者函数
用 x 来 fulfill promise
代码实现
// 三种状态
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
// 从new Promise((resolve,rejected) => {}) 切入
class _promise {
constructor(exector) {
this.state = PENDING;
this.value = null;
this.reason = null;
// then函数中成功失败回调函数的集合, 这样支持同一个promise实例调用多个then
this.onFulfilled = [];
this.onRejected = [];
try {
exector(this.resolve, this.reject);
} catch (e) {
this.reject(e);
}
}
resolve = value => {
if (this.state === PENDING) {
this.state = FULFILLED;
this.value = value;
this.onFulfilled.forEach(fn => fn(value));
}
};
reject = reason => {
if (this.state === PENDING) {
this.state = REJECTED;
this.reason = reason;
this.onRejected.forEach(fn => fn(reason));
}
};
then = (onFulfilled, onRejected) => {
// 根据规范,如果参数不是函数就忽略,使用默认函数
onFulfilled =
typeof onFulfilled === 'function'
? onFulfilled
: function (x) {
return x;
};
onRejected =
typeof onRejected === 'function'
? onRejected
: function (e) {
throw e;
};
// 根据贵燃then可以链式调用,返回的也是promise实例
const promise = new _promise((resolve, reject) => {
// 创建成功和失败的微任务回调
const fulfilledTask = () => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
};
const rejectedTask = () => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
};
// 根据三种状态做不同的处理
if (this.state === FULFILLED) {
fulfilledTask();
} else if (this.state === REJECTED) {
rejectedTask();
} else {
this.onFulfilled.push(fulfilledTask);
this.onRejected.push(rejectedTask);
}
});
return promise;
};
}
// 对then中return出来的数据做处理
// 1. x 不能等于 promise 自身, 不然会报错等于死循环
// 2. x 为对象或者函数
// 3. x 为普通值,直接resolve
function resolvePromise(promise, x, resolve, reject) {
if (promise === x) {
return reject(new TypeError('type error'));
}
if ((typeof x === 'object' || typeof x === 'function') && x !== null) {
let called;
try {
let then = x.then;
if (typeof then === 'function') {
then.call(
x,
y => {
if (called) return;
called = true;
resolvePromise(promise, y, resolve, reject);
},
e => {
if (called) return;
called = true;
reject(e);
}
);
} else {
resolve(x);
}
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
resolve(x);
}
}
// 下面是使用 promises-aplus-tests 进行测试的代码
_promise.defer = _promise.deferred = function () {
let dfd = {};
dfd.promise = new _promise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
};
module.exports = _promise;
/* ---------------------- 其他方法简单实现 ---------------------- */
class {
static resolve(vlaue) {
if (value instanceof _promise) {
return value;
}
return new _promise(resolve => resolve(value));
}
static reject(reason) {
return new _promise((resolve, reject) => reject(reason));
}
static all(promises) {
return new _promise((resolve, reject) => {
let index = 0,
len = promises.length,
result = [];
for (const item of promises) {
_promise.resolve(item).then(
res => {
result[index++] = res;
if (index === len) {
resolve(result);
}
},
err => reject(err)
);
}
});
}
static race(promises) {
return new _promise((resolve, reject) => {
for (const item of promises) {
_promise.resolve(item).then(
res => resolve(res),
err => reject(err)
);
}
});
}
static allSettled(promises) {
const promiseArr = Array.from(promises);
return _Promise.all(
promiseArr.map(p =>
_Promise.resolve(p).then(
res => {
return { status: 'fulfilled', value: res };
},
error => {
return { status: 'rejected', reason: error };
}
)
)
);
}
}