最近参照Promise/A+规范实现了一个自己的Promise,代码如下:
const enum PromiseState {
PENDING = "pending",
FULFILLED = "fulfilled",
REJECTED = "rejected",
}
export class PromiseLike {
private _state: PromiseState;
private _value: any; // 被决议的终值
private _callbacks: any[]; // then中被注册的回调
constructor(exector: (resolve, reject) => any) {
this._state = PromiseState.PENDING;
this._value = undefined;
this._callbacks = [];
const resolve = value => {
if (this._state !== PromiseState.PENDING) return;
this._state = PromiseState.FULFILLED;
this._value = value;
createMicroTask(() => {
this._callbacks.forEach(cb => {
cb();
});
});
};
const reject = reason => {
if (this._state !== PromiseState.PENDING) return;
this._state = PromiseState.REJECTED;
this._value = reason;
createMicroTask(() => {
this._callbacks.forEach(cb => {
cb();
});
});
};
exector(resolve, reject);
}
then(onfulfilled?, onrejected?) {
const onFulfilled = typeof onfulfilled === "function" ? onfulfilled : undefined;
const onRejected = typeof onrejected === "function" ? onrejected : undefined;
const returnValue = new PromiseLike((resolve, reject) => {
const callback = () => {
// 如果Promise已决议,不重复执行
if (this._state === PromiseState.PENDING) {
return;
} else {
let fn;
// onFulfilled或onRejected为undefined则被忽略
if (this._state === PromiseState.FULFILLED) {
if (onFulfilled) {
fn = onFulfilled;
} else {
resolve(this._value);
return;
}
} else if (this._state === PromiseState.REJECTED) {
if (onRejected) {
fn = onRejected;
} else {
reject(this._value);
return;
}
}
let result;
try {
result = fn.call(undefined, this._value);
} catch (e) {
reject(e);
return;
}
resolution(returnValue, result, resolve, reject);
}
};
if (this._state !== PromiseState.PENDING) {
createMicroTask(callback);
} else {
this._callbacks.push(callback);
}
});
return returnValue;
}
}
// [[Resolve]](promise,x)
function resolution(promise, value, resolve, reject) {
if (promise === value) {
reject(new TypeError("PromiseLike: promise cannot have the same reference with value"));
return;
} else if (typeof value === "function" || (value && typeof value === "object")) {
let then;
try {
then = value.then;
} catch (e) {
reject(e);
return;
}
if (typeof then === "function") {
let called = false;
const resolvePromise = val => {
if (called) return;
called = true;
resolution(promise, val, resolve, reject);
};
const rejectPromise = res => {
if (called) return;
called = true;
reject(res);
};
try {
then.call(value, resolvePromise, rejectPromise);
} catch (e) {
rejectPromise(e);
}
} else {
resolve(value);
}
} else {
resolve(value);
}
}
function createMicroTask(fn) {
queueMicrotask(fn);
}
// 测试用
// export const adapter = {
// deferred() {
// let res, rej;
// const promise = new PromiseLike((resolve, reject) => {
// res = resolve;
// rej = reject;
// });
// return {
// promise,
// resolve: res,
// reject: rej,
// };
// },
// };
// module.exports = adapter;
这个代码通过了Promise/A+的所有测试用例。但是有这么一道关于Promise的面试题:
new Promise<void>((resolve, reject) => {
resolve();
})
.then(() => {
console.log(0);
return new Promise((resolve, reject) => {
resolve(4);
});
})
.then(res => {
console.log(res);
});
new Promise<void>((resolve, reject) => {
resolve();
})
.then(() => {
console.log(1);
})
.then(() => {
console.log(2);
})
.then(() => {
console.log(3);
})
.then(() => {
console.log(5);
})
.then(() => {
console.log(6);
});
问代码的输出应该是什么。这道题的输出应该是0 1 2 3 4 5 6
,但是用我自己写的PromiseLike
,输出为:0 1 2 4 3 5 6
。我在网上也找了几个别人实现的Promise,输出两种情况都有。所以我的问题是:
- 是在哪一步出了问题导致结果不一样
通过Promise/A+所有测试用例的Promise就是一个标准的Promise实现吗
2.1 如果是,那这种输出不一样的情况是因为规范有遗漏吗
2.2 如果不是,是因为Promise/A+官方测试用例有没覆盖到的情况吗
谢谢大家了
你这里有两套 promise 。promise/A+ 是不关心这两套 promise 之间的执行顺序关系的。
你可以对一下 promise/A+ ,这两个结果应该都是符合规范的。只能说你的实现与 javascript 可能有点不一样。仅参照 promise/A+ ,两套 Promise 存在的时候是有很多中不同可能结果的。因为 promise/A+ 根本没有规定两个无关的 promise 之间的执行顺序。
javascript 标准是仔细规定了 promsise 的行为的。它的标准是在规定整个程序的行为,promise 只是其中的一部分。按照 javascript 标准,肯定是有唯一结果的。
所以,你的 promiseLike 符合 promise/A+ 规范,但是跟 javacript 标准并不完全相同。
比如,javascprit 里,用 Promise resolve promise 的时候会产生一个微任务:15. Perform
HostEnqueuePromiseJob(job.[[Job]], job.[[Realm]])
.,但是你的实现并没有。但是,是否产生微任务都不违反 promise/A+ 。