6d5421771e8e345dfc962a95b73aa9cc.jpeg
/**
* 检查目标是否为thenable对象
* 任何对象,只要有then方法,那么它就可以被作为 Promise 实例去处理(鸭子辩型),所以这里的 Xpromise 可以跟原生 Promise 相互调用使用:
* Promise.resolve(123).then(() => XPromise.resolve(24)).then(v => console.log(v))
*/
function isThenable(obj) {
if (obj !== null && (typeof obj === 'object' || typeof obj === 'function') && typeof obj.then === 'function'){
return true;
}
return false;
}
function typeOf(obj) {
return Object.prototype.toString.call(obj).toLowerCase().slice(8,-1);
}
/**
* 决议程序!!!
*/
function resolutionProcedure(promise, x, resolve, reject) {
if (promise === x) {
reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
}else if(x instanceof XPromise) {
x.then(resolve, reject);
} else if (typeof x === 'object' && x !== null || typeof x === 'function') {
try {
const then = x.then;
if (typeof then === 'function') {
// 记录 resolvePromise 或 rejectPromise 是否被调用过(或者说记录then是否被调用过)
// 如果 resolvePromise 和 rejectPromise 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
// 当然这里加不加也无所谓的,因为在XPromise构造函数里就限定了,它的状态只能变更一次,后面的直接无视
// 没加这个判断顶多就是做些无用功
let called = false;
try {
// 如果 then 是函数,将 x 作为函数的作用域 this 调用之。传递两个回调函数作为参数,第一个参数叫做 resolvePromise ,第二个参数叫做 rejectPromise
then.call(
x,
function resolvePromise(y) {
if (called) return;
called = true;
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
resolutionProcedure(promise, y, resolve, reject);
},
function rejectPromise(r){
if (called) return;
called = true;
reject(r);
}
)
} catch (e) {
if (called) return;
called = true;
reject(e);
}
} else {
// 如果 then 不是函数,以 x 为参数执行 promise
resolve(x);
}
}catch(e){
// 如果取 x.then 的值时抛出错误 e ,则以 e 为据因拒绝 promise
reject(e);
return
}
} else {
// 如果 x 不为对象或者函数,以 x 为参数执行 promise
resolve(x);
}
}
const defer = setTimeout;
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const PROP_VALUE = Symbol('value');
const PROP_STATUS = Symbol('status');
const PROP_REASON = Symbol('reason');
const QUEUE_FULFILLED = Symbol('fulfilledQueue');
const QUEUE_REJECTED = Symbol('rejectedQueue');
class XPromise {
[PROP_STATUS] = PENDING;
[QUEUE_FULFILLED] = [];
[QUEUE_REJECTED] = [];
constructor(executor){
const type = typeof executor;
if (type !== 'undefined' && type !== 'function') {
throw new TypeError(`Promise resolver ${type} is not a function`);
}
const resolve = (value) => {
if (this[PROP_STATUS] !== PENDING) return;
this[PROP_VALUE] = value;
this[PROP_STATUS] = FULFILLED;
this[QUEUE_FULFILLED].forEach(func => func(value));
}
const reject = (e) => {
if (this[PROP_STATUS] !== PENDING) return;
this[PROP_REASON] = e;
this[PROP_STATUS] = REJECTED;
this[QUEUE_REJECTED].forEach(func => func(e));
}
try {
if (executor) executor(resolve, reject);
} catch(e) {
reject(e);
}
}
then(onFulfilled, onRejected) {
if (typeof onFulfilled !== 'function') onFulfilled = function onFulfilled(v){return v};
if (typeof onRejected !== 'function') onRejected = function onRejected(e){throw e};
const self = this;
const resolveByStatus = (resolve, reject) => {
defer(function resolveDeferFunc(){
try {
const x = onFulfilled(self[PROP_VALUE]);
resolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
const rejectByStatus = (resolve, reject) => {
defer(function rejectDeferFunc(){
try {
const x = onRejected(self[PROP_REASON]);
resolutionProcedure(promise2, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
const promise2 = new XPromise(function executor(resolve, reject){
switch (self[PROP_STATUS]) {
case PENDING:
self[QUEUE_FULFILLED].push(function fulfilledFn(){
resolveByStatus(resolve, reject);
});
self[QUEUE_REJECTED].push(function rejectedFn(){
rejectByStatus(resolve, reject);
});
break;
case FULFILLED:
resolveByStatus(resolve, reject);
break;
case REJECTED:
rejectByStatus(resolve, reject);
break;
default:
break
}
});
return promise2;
}
// Promise/A+ 规范并没有规定要实现对象的 catch,finally及resolve,reject,all...等静态方法,
// 手写 Promise 实现 then 方法即可,其他一切都是在 constructor + then 的基础上实现的
// 这里为了学习需要,按照 MDN 列举的几个方法都撸了一遍
catch(onRejected) {
return this.then(undefined, onRejected);
}
finally(onFinally) {
// 与Promise.resolve(2).then(() => {}, () => {}) (resolved的结果为undefined)不同,Promise.resolve(2).finally(() => {}) resolved的结果为 2。
// 同样,Promise.reject(3).then(() => {}, () => {}) (resolved 的结果为undefined), Promise.reject(3).finally(() => {}) rejected 的结果为 3
// [注] 在finally回调中 throw(或返回被拒绝的 promise)将以 throw() 指定的原因拒绝新的promise.
return this.then(
value => {
return XPromise.resolve(onFinally()).then(() => value);
},
reason => {
return XPromise.resolve(onFinally()).then(() => {throw reason});
}
);
}
static resolve(value) {
// 如果是Promise实例,直接返回该对象
if (value instanceof XPromise) return value;
// !!如果是thenable对象,用resolve、reject作为参数调用它的then方法
if (isThenable(value)) {
return new XPromise((resolve, reject) => value.then(resolve, reject));
}
return new XPromise(resolve => resolve(value));
}
static reject(reason) {
return new XPromise((_, reject) => reject(reason));
}
static all(iterable) {
return new XPromise(function allExecutor(resolve, reject){
const res = [];
let count = 0; // 记录当前迭代的数据量
let resolvedCount = 0;
function resolveRes(index, value) {
res[index] = value;
resolvedCount += 1;
// console.log(resolvedCount, count, index, value);
if (resolvedCount >= count) {
resolve(res);
}
}
// 任何可迭代对象都可以处理
for (const item of iterable) {
count++;
// 注意对thenable对象的处理!!
// item instanceof XPromise 语句可以不用写,它肯定是thenable的,同时在XPromise.resolve会检查直接返回这个对象
(function(index){
if (item instanceof XPromise || isThenable(item)) {
XPromise.resolve(item).then(
value => resolveRes(index, value),
reason => reject(reason),
);
} else {
resolveRes(index, item);
}
})(count-1)
}
if (count === 0) resolve([]);
});
}
static allSettled(iterable) {
return new XPromise(function allSettledExecutor(resolve, reject){
const res = [];
let count = 0;
let resolvedCount = 0;
function resolveRes(index, value) {
res[index] = value;
resolvedCount += 1;
if (resolvedCount >= count) {
resolve(res);
}
}
for (const item of iterable) {
count++;
(function(index){
if (isThenable(item)) {
XPromise.resolve(item).then(
value => resolveRes(index, {status: FULFILLED, value}),
reason => resolveRes(index, {status: REJECTED, reason}),
);
} else {
resolveRes(index, {status: FULFILLED, value: item});
}
})(count-1);
}
if (count === 0) resolve([]);
});
}
static any(iterable) {
return new XPromise(function anyExecutor(resolve, reject){
let count = 0;
let resolvedCount = 0;
const errors = [];
function rejectRes(index, reason) {
resolvedCount += 1;
errors[index] = reason;
if (resolvedCount >= count) {
reject(new AggregateError(errors, 'All promises were rejected'));
}
}
for (const item of iterable) {
count++;
(function(index){
if (isThenable(item)) {
XPromise.resolve(item).then(
value => resolve(value),
reason => rejectRes(index, reason),
);
} else {
resolve(item);
}
})(count-1)
}
if (count === 0) reject(new AggregateError([], 'All promises were rejected'));
});
}
static race(iterable) {
return new XPromise(function raceExecutor(resolve, reject){
for (const item of iterable) {
if (isThenable(item)) {
XPromise.resolve(item).then(
value =>resolve(value),
reason => reject(reason),
);
} else {
resolve(item);
}
}
});
}
}
代码测试
参考文献