注意,代码实现并不是 polyfill,因为微任务是浏览器层面的实现,这里我们只是去实现规范。
type PromiseState = 'pending' | 'fulfilled' | 'rejected';
type PromiseResolve<T> = (value: T) => void;
type PromiseReject = (reason?: any) => void;
class CustomPromise<T> {
PromiseState: PromiseState = 'pending';
PromiseResult?: any;
private onFulfilledCallbacks: PromiseResolve<any>[] = [];
private onRejectedCallbacks: PromiseReject[] = [];
private resolve(value: T): void {
if (this.PromiseState === 'pending') {
this.PromiseState = 'fulfilled';
this.PromiseResult = value;
for (const callback of this.onFulfilledCallbacks) {
callback(value);
}
}
}
private reject(reason?: any): void {
if (this.PromiseState === 'pending') {
this.PromiseState = 'rejected';
this.PromiseResult = reason;
for (const callback of this.onRejectedCallbacks) {
callback(reason);
}
}
}
constructor(executor: (resolve: PromiseResolve<T>, reject: PromiseReject) => void) {
try {
executor(this.resolve.bind(this), this.reject.bind(this));
} catch (e) {
this.reject(e);
}
}
then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
): CustomPromise<TResult1 | TResult2> {
const promise = new CustomPromise<TResult1 | TResult2>((resolve, reject) => {
switch (this.PromiseState) {
case 'fulfilled':
queueMicrotask(() => {
if (typeof onfulfilled === 'function') {
try {
const x = onfulfilled(this.PromiseResult);
promiseResolutionProcedure(promise, x, resolve, reject);
} catch (e) {
reject(e);
}
} else {
resolve(this.PromiseResult);
}
});
break;
case 'rejected':
queueMicrotask(() => {
if (typeof onrejected === 'function') {
try {
const x = onrejected(this.PromiseResult);
promiseResolutionProcedure(promise, x, resolve, reject);
} catch (e) {
reject(e);
}
} else {
reject(this.PromiseResult);
}
});
break;
case 'pending':
queueMicrotask(() => {
this.onFulfilledCallbacks.push((value) => {
if (typeof onfulfilled === 'function') {
try {
const x = onfulfilled(this.PromiseResult);
promiseResolutionProcedure(promise, x, resolve, reject);
} catch (e) {
reject(e);
}
} else {
resolve(value);
}
});
this.onRejectedCallbacks.push((reason) => {
if (typeof onrejected === 'function') {
try {
const x = onrejected(this.PromiseResult);
promiseResolutionProcedure(promise, x, resolve, reject);
} catch (e) {
reject(e);
}
} else {
reject(reason);
}
});
});
break;
default:
break;
}
});
return promise;
}
}
function promiseResolutionProcedure(promise: CustomPromise<any>, x: any, resolve: PromiseResolve<any>, reject: PromiseReject) {
if (x === promise) {
reject(new TypeError('Chaining cycle detected for promise'));
} else if (x instanceof CustomPromise) {
switch (x.PromiseState) {
case 'pending':
x.then((value) => {
promiseResolutionProcedure(promise, value, resolve, reject);
});
break;
case 'fulfilled':
resolve(x.PromiseResult);
break;
case 'rejected':
reject(x.PromiseResult);
break;
default:
break;
}
} else if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
let isCalled = false;
try {
const then = x.then;
if (typeof then === 'function') {
then.call(
x,
(y: any) => {
if (!isCalled) {
isCalled = true;
promiseResolutionProcedure(promise, y, resolve, reject);
}
},
(r?: any) => {
if (!isCalled) {
isCalled = true;
reject(r);
}
}
);
} else {
resolve(x);
}
} catch (e) {
if (!isCalled) {
isCalled = true;
reject(e);
}
}
} else {
resolve(x);
}
}