标签:imp each ret 可变 用法 yun http The 测试方法
写一个符合 Promise A+
规范的 Promise
// MyPromise.ts
type resType = (value?: any) => void;
type rejType = (reason?: any) => void;
type executorType = (res: resType, rej?: rejType) => void;
type onfulfilledType = (value?: any) => any;
type onrejectedType = (reason?: any) => any;
/**
* `promise` 的三个状态
* 分别有 `pending`, `fulfilled`, `rejected`
*/
enum stat {
PENDING,
FUIFILLED,
REJECTED,
}
interface MyPromise {
value: any,
reason: any,
status: stat,
onfulfilledCallbacks: onfulfilledType[],
onrejectedCallbacks: onrejectedType[],
}
要完成 Promise
的功能其实不一定要微任务,只要是异步执行的就可以
不过我这里用的是 node.js
特供 process.nextTick()
,属于微任务
运行 promises-aplus-tests
进行测试最后会显示运行时长,可以对比一下与 setTimeout()
的差距
// MyPromise.ts
function asyncExecute(cb: () => void): void {
// setTimeout(cb) 也可以
process.nextTick(cb);
}
主要工作是写三个方法,整体思路就是发布-订阅模式
then
负责订阅状态的变化,注册相应事件;而 resolve/reject
负责更改状态,发布这一事件
resolvePromise
用于处理 onfulfilled/onrejected
的返回值
// MyPromise.ts
class MyPromise implements MyPromise {
constructor(executor: executorType) {
// code ...
}
private static resolvePromise(bridgePromise: MyPromise, target: any, resolve: resType, reject: rejType): void {
// code ...
}
then(onfulfilled?: any, onrejected?: any): MyPromise {
// code ...
}
}
把 resolve()/reject()
想象成 emit()
会好理解些
constructor(executor: executorType) {
this.value = undefined; // fulfilled 状态 返回的值
this.reason = undefined; // rejected 状态 拒绝的原因
this.status = stat.PENDING; // 初始状态
this.onfulfilledCallbacks = []; // 存储 onfulfilled 回调
this.onrejectedCallbacks = []; // 存储 onrejected 回调
const reject: rejType = reason => {
if (this.status !== stat.PENDING) return;
asyncExecute(() => {
this.status = stat.REJECTED;
this.reason = reason;
this.onrejectedCallbacks.forEach(cb => cb(this.reason));
});
}
const resolve: resType = value => {
if (this.status !== stat.PENDING) return;
asyncExecute(() => {
this.status = stat.FUIFILLED;
this.value = value;
this.onfulfilledCallbacks.forEach(cb => cb(this.value));
});
}
try {
executor(resolve, reject);
} catch (e) {
reject(e);
}
}
想象成 EventEmitter
的 on()
,订阅实例的 onfulfilled/onrejeced
事件,以bridgePromise
的 resolve()/reject()
作为其回调
/**
* 注册fulfilled状态/rejected状态对应的回调函数,本质上是个发布-订阅模式
* @param onfulfilled fulfilled状态时 执行的函数
* @param onrejected rejected状态时 执行的函数
* @returns 返回新的MyPromise
*/
then(onfulfilled?: any, onrejected?: any): MyPromise {
// 返回值穿透
onfulfilled = typeof onfulfilled === "function" ? onfulfilled : (value: any) => value;
// 错误冒泡的关键
onrejected = typeof onrejected === "function" ? onrejected : (reason: any) => { throw reason };
const bridgePromise = new MyPromise((resolve, reject) => {
// pending: 订阅当前实例的状态变更
if (this.status === stat.PENDING) {
this.onfulfilledCallbacks.push(value => {
try {
const x: any = onfulfilled(value);
MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
this.onrejectedCallbacks.push(reason => {
try {
const x: any = onrejected(reason);
MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
// fulfilled: 变更 bridgePromise 状态为 fulfilled
// 如果 onfulfilled 不是函数就会将 this.value 传给 x, 即返回值穿透的原理
} else if (this.status === stat.FUIFILLED) {
asyncExecute(() => {
try {
const x: any = onfulfilled(this.value);
MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
// rejected: 变更 bridgePromise 状态为 rejected
// 一般来说直接 throw reason, 被捕获异常走 reject 这条路, 即错误冒泡的原理
} else {
asyncExecute(() => {
try {
const x: any = onrejected(this.reason);
MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
} catch (e) {
reject(e);
}
});
}
});
// 链式调用的关键
return bridgePromise;
}
个人感觉这里是最难的地方,可以看到上面的then
方法中,没有直接以 onfulfilled/onrejected
的返回值作为 bridgePromise
的不可变值(指 value/reason
) ,而是根据其类型分别进行处理
Promise a+
对这里的处理有比较多的要求,需要注意的点我都注释了需求的编号,可以到这里查看需求
/**
* resolve中的值几种情况:
* 1.普通值
* 2.promise对象
* 3.thenable对象/函数
*/
/**
* 对resolve进行改造 针对resolve中不同值情况分别进行处理
* @param bridgePromise then将要返回的新的MyPromise对象
* @param target then中onfulfilled/onrejected的返回值
* @param resolve bridgePromise的resolve方法
* @param reject bridgePromise的reject方法
*/
private static resolvePromise(bridgePromise: MyPromise, target: any, resolve: resType, reject: rejType): void {
// promise aplus 2.3.1
if (target === bridgePromise) {
return reject(new TypeError("循环引用"));
}
// promise aplus 2.3.3.3.3 防止多次调用
let called = false;
// promise aplus 2.3.2
if (target instanceof MyPromise) {
// promise aplus 2.3.2.1
if (target.status === stat.PENDING) {
target.then((x: any) => {
MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
}, reject);
} else { // promise aplus 2.3.2.2 & 2.3.2.3
target.then(resolve, reject);
}
} else if (target && (typeof target === "object" || typeof target === "function")) {
// promise aplus 2.3.3
try {
const then = target.then;
// promise aplus 2.3.3.3
if (typeof then === "function") {
then.call(target, (x: any) => {
if (called) return;
called = true;
MyPromise.resolvePromise(bridgePromise, x, resolve, reject);
}, (reason: any) => {
if (called) return;
called = true;
reject(reason);
});
} else {
resolve(target);
}
} catch (e) { // promise aplus 2.3.3.2
if (called) return;
called = true;
reject(e);
}
} else {
resolve(target);
}
}
??比较懒,我直接在同一文件下执行这个测试方法
测试工具 github
:https://github.com/promises-aplus/promises-tests ,README
写了大致用法
// MyPromise.ts
// test
interface defer {
promise?: MyPromise,
resolve?: resType,
reject?: rejType,
}
const adapter = {
deferred() {
let def: defer = {};
def.promise = new MyPromise((resolve, reject) => {
def.resolve = resolve;
def.reject = reject;
});
return def;
}
}
// 下载 promises aplus 测试工具
// github: https://github.com/promises-aplus/promises-tests
// npm install promises-aplus-tests -g
// 直接运行 typescript:
// npm install ts-node -g
// ts-node MyPromise.ts
var promisesAplusTests = require("promises-aplus-tests");
promisesAplusTests(adapter, console.log);
执行代码:
ts-node MyPromise.ts
完成啦,不过还有一些 Promise
的方法没有实现,比如 race/all
等,Promise a+
没有做要求,就先不在这里介绍了。
可以参考三元的博客,宝藏级前端大佬。
标签:imp each ret 可变 用法 yun http The 测试方法
原文地址:https://www.cnblogs.com/hhteng/p/14672477.html