实现 Promise A+ 规范
好像面试会考这个,于是开始尝试手写一波… 结果全是错,只能过 base 的测试用例,太真实了,还是去官网先复制一个研究下,我看了 Adehun
这个版本的,代码解耦的很棒,正好可以用来当模板练习。
1. Promise 基本使用
基本上 Promise 是有两种状态:确定态 和 不确定态, 没有按照三状态来解释,明白意思就好!其中,状态一旦转为确定态就不可以改变了。
Promise 最常使用到的就是它的 then
方法,是注册异步回调的专有接口,它的好处有:回调函数不用写在参数的位置,使其更加声明式的挂载,还可以进行链式的 promise 调用。
2. Promise 的理解
写完整体 Promise A+ 代码后,感觉对 promise 又有了新的理解:
- 想要获取一个 promise 的结果必须新建一个 newPromise 去订阅它
- 基于上一步,promise 在发出通知的时候,必须保持异步,这样才能保证先订阅成功再统一触发通知
- 多个 promise 之间的订阅关系:上一个 promise 的回调是下一个 promise 状态的生产者
3. 设计思路
感觉先梳理个大体的轮廓再写比较好,不然写着写着就不知道下一步该写啥了。我对着 Adehun
这个版本的代码画个大体的流程:
4. 代码实现
ok, 接下来就顺着流程走就行了。
const enum State {
PENDING = 0,
FULFILLED = 1,
REJECTED = 2,
}
const Utils = {
isFunction: (x: any): x is Function => x && typeof x === 'function',
isObject: (x: any): x is object => x && typeof x === 'object',
isPromise: (x: any): x is MyPromise => x instanceof MyPromise,
execAsync: (fn: Function) => setTimeout(fn),
};
const onFulfilledDefault = function (result: any) {
return result;
};
const onRejectDefault = function (reason: any) {
throw reason;
};
const Resolve = function (p: MyPromise, x: any) {
if (p === x) {
p.rejected(
new TypeError("promise and it'value can't refer to same object")
);
return;
}
if (Utils.isPromise(x)) {
if (x.state === State.PENDING) {
x.then(
function (y: any) {
Resolve(p, y);
},
function (r: any) {
p.rejected(r);
}
);
} else {
p.transition(x.state, x.value);
}
return;
}
if (Utils.isFunction(x) || Utils.isObject(x)) {
let flag = false;
try {
const then = x.then;
if (Utils.isFunction(then)) {
then.call(
x,
function (y: any) {
if (flag) return;
flag = true;
Resolve(p, y);
},
function (r: any) {
if (flag) return;
flag = true;
p.rejected(r);
}
);
} else {
p.fulfilled(x);
}
} catch (e) {
if (flag) return;
flag = true;
p.rejected(e);
}
return;
}
p.fulfilled(x);
};
type Handler = {
resolve: null | Function;
reject: null | Function;
};
class MyPromise {
state: State = State.PENDING;
value: any = undefined;
queue: MyPromise[] = [];
handler: Handler = {
resolve: null,
reject: null,
};
constructor(fn?: (resolve: Function, reject: Function) => void) {
if (fn) {
let flag = false;
const that = this;
fn(
function (y: any) {
if (flag) return;
flag = true;
Resolve(that, y);
},
function (r: any) {
if (flag) return;
flag = true;
that.rejected(r);
}
);
}
}
inform() {
Utils.execAsync(() => {
while (this.queue.length) {
const p = this.queue.shift() as MyPromise;
const handler =
this.state === State.FULFILLED
? p.handler.resolve || onFulfilledDefault
: p.handler.reject || onRejectDefault;
try {
const res = handler(this.value);
Resolve(p, res);
} catch (e) {
p.rejected(e);
}
}
});
}
then(onFulfilled: Function, onrejected: Function) {
const p2 = new MyPromise();
if (Utils.isFunction(onFulfilled)) {
p2.handler.resolve = onFulfilled;
}
if (Utils.isFunction(onrejected)) {
p2.handler.reject = onrejected;
}
this.queue.push(p2);
if (this.state !== State.PENDING) {
this.inform();
}
return p2;
}
transition(state: State, value: any) {
if (this.state !== State.PENDING) return;
this.state = state;
this.value = value;
this.inform();
}
rejected(reason: any) {
this.transition(State.REJECTED, reason);
}
fulfilled(result: any) {
this.transition(State.FULFILLED, result);
}
}
// 测试
const adapter = {
resolved(x: any) {
return new MyPromise((resolve: any, reject: any) => {
resolve(x);
});
},
rejected(x: any) {
return new MyPromise((resolve: any, reject: any) => {
reject(x);
});
},
deferred() {
let resolve, reject;
return {
promise: new MyPromise((rev: any, rej: any) => {
resolve = rev;
reject = rej;
}),
resolve,
reject,
};
},
};
const tests = require('promises-aplus-tests');
tests(adapter, (err: any) => {
console.log(err);
});
5. 总结
一开始写的时候,promises-aplus-tests
报了很多的错,抓耳挠腮就是好一阵,总结下单词拼写可以使用 Code Spell Checker
, 类型传参错误可以使用 AnyScript
, 还有就是处理细节的错误,尤其是 Thenable
的处理很容易写漏逻辑,一开始还抱怨为什么还有同化异构的 promise
一说,写了几遍发现代码其实就加了一个 try-catch
和 flag
的复杂度。
总的来说,写好一个完全通过 promises-aplus-tests
测试的代码还是挺有挑战的事,Adehun
这个版本没有使用一些高级的 js 语法,像函数式,bind, apply
等,用到的就是普通的函数和常规的流程控制语句,但是各个部分设计的都很合理,也契合规范。