先看看Promise的代码转换为callback风格的话是什么样的,更方便后续的参考
// promise
const prom = function (arg: string) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(arg);
}, 1000);
});
};
prom("a").then((fulfilled) => {
console.log(fulfilled);
});
// callback
const cbfunc = function (arg: string, cb: (arg: string) => void) {
setTimeout(() => {
cb(arg);
}, 1000);
};
cbfunc("b", (arg) => console.log(arg));
稍做分析,两者都是在异步中进行插入函数,不过callback是直接插入需要执行的代码,promise通过resolve函数收集需要的数据后在then中的回调中暴露出来,基于这个特性就能实现一个基本的构造函数了
构造函数
class MyPromise<T> {
private value!: T;
private state: "pendding" | "fulfilled" | "rejected";
constructor(args: (resolve: (value: T) => void, reject: (value: any) => void) => void) {
this.state = "pending"; // 初始为pending
const resolve = (value: T) => {
if (this.state == "pending") {
this.value = value;
this.state = "fulfilled";
}
};
const reject = (value: any) => {
/* ... */
};
args(resolve, reject);
}
// 为了实现then的链式调用,这里then返回一个新的实例
then<R>(onFulfilled: (fulfilled: T) => R): MyPromise<R> {
return new MyPromise((resolve, _reject) => {
switch (this.state) {
case "fulfilled":
resolve(onFulfilled(this.value));
break;
case "rejected":
/* ... */
break;
case "pendding":
/* ... */
break;
}
});
}
}
这个类现在已经能够收集参数并且通过它的then方法来获得收集的参数,但是因为then是在同步运行,如果在异步中则收集不到数据,只能实现下面的这种用法
let mp = new MyPromise<number>((resolve) => {
resolve(123);
});
mp.then((fulfilled) => {
console.log(fulfilled);
return "456";
}).then((fulfilled) => {
console.log(fulfilled);
});
为了能处理异步数据,首先需要把resolve方法中的行为全部扔到下一个事件循环中,(这里用了settimeout放到了宏任务队列中,实际的是放到了微任务队列中),这时就会让then方法比实例化时的resolve更快执行,这样只需要在then中收集到resolve reject时需要执行的函数,再在实例化的resolve收集到数据后执行就可以了
一个例子讲解大概的流程
let p1 = new MyPromise<number>((resolve) => {
console.log("init");
setTimeout(() => {
resolve(1);
}, 1000);
});
let p2 = p1.then((fulfilled) => {
console.log(fulfilled);
return "2";
});
p2.then((fulfilled) => {
console.log(fulfilled);
});
- 执行构造函数中的同步内容,即打印
init - 执行
p1的then,收集传入的回调函数,同时返回出p2,p2继续同步收集then中的回调函数 - 执行构造函数中的异步内容,同时让
resolve收集到数据 - 在下一个事件循环中再将收集到的数据传给
p1的then收集的回调函数并执行该回调 - 将回调的返回值作为
p2的构造函数中的resolve接收的参数,然后同第四步 - 如果出现错误则用
reject函数接收参数,同时只能使用then中的第二个函数接收
具体实现代码,相比上面的resolve的流程,reject需要多添加一个错误传递的功能,具体见代码中的注释
class MyPromise<T> {
private value!: T;
private reason!: any;
private state: "pending" | "fulfilled" | "rejected";
private successWaiting: ((fulfilled: any) => void)[];
private faildWaiting: ((reason: any) => void)[];
constructor(argFunc: (resolve: (value: T | MyPromise<T>) => void, reject: (value: any) => void) => void) {
this.state = "pending";
this.reason = undefined;
this.successWaiting = [];
this.faildWaiting = [];
// 实例化时这两个函数不会执行
// 它们在调用处的下一次事件循环中执行 这时候接收到resolve的数据
// 然后把这个对象的then接收的回调函数执行
const resolve = (value: T | MyPromise<T>) => {
setTimeout(() => {
if (this.state == "pending") {
if (value instanceof MyPromise) {
value.then(resolve); // 如果接收一个promise 在它完成后再resolve
} else {
this.state = "fulfilled";
this.value = value;
for (const func of this.successWaiting) {
func(this.value); // 这里执行then接收的回调
}
}
}
}, 0);
};
const reject = (value: any) => {
setTimeout(() => {
if (this.state == "pending") {
if (value instanceof MyPromise) {
value.catch(reject);
} else {
this.state = "rejected";
this.reason = value;
for (const func of this.faildWaiting) {
func(this.reason);
}
}
}
}, 0);
};
argFunc(resolve, reject);
}
then<RT, RF = never>(onfulfilled: (fulfilled: T) => RT | MyPromise<RT>, onrejected?: (reason: any) => RF | MyPromise<RF>): MyPromise<RT | RF> {
return new MyPromise<RT | RF>((resolve, reject) => {
const successFunc = (value: T) => {
try {
const result = onfulfilled(value);
// 如果回调中新返回一个promise 在返回的promise完成时resolve结果
if (result instanceof MyPromise) {
result.then(resolve, reject);
} else {
resolve(result);
}
} catch (e) {
// 如果在函数中抛出错误 在这里reject 它会在下一个proise中挂载reason
reject(e);
}
};
const faildFunc = (reason: any) => {
try {
if (onrejected) {
const result = onrejected(reason);
if (result instanceof MyPromise) {
result.catch(reject);
} else {
resolve(result);
}
} else {
reject(this.reason); // 如果没有错误处理函数,就继续抛出错误
}
} catch (e) {
reject(e);
}
};
switch (this.state) {
// 调用时在pendding状态,将处理过程保存,等到这个实例中的resolve或reject中执行
case "pending":
this.successWaiting.push(successFunc);
this.faildWaiting.push(faildFunc);
break;
case "fulfilled":
successFunc(this.value);
break;
case "rejected":
faildFunc(this.reason);
break;
}
});
}
// catch中的行为和then中处理reject一样
catch<R>(onrejected: (e: any) => R | MyPromise<R>): MyPromise<R> {
return new MyPromise<R>((resolve) => {
if (this.state == "rejected") {
// catch的返回值也是通过then的第一个回调获得
resolve(onrejected(this.reason));
} else {
this.faildWaiting.push((reason: any) => resolve(onrejected(reason)));
}
});
}
finally(callBack: () => void) {
return new MyPromise<never>(() => {
switch (this.state) {
case "pending":
this.successWaiting.push(callBack);
this.faildWaiting.push(callBack);
break;
case "fulfilled":
case "rejected":
callBack();
break;
}
});
}
}
静态方法
Promise.resolve Promise.reject
都是返回一个接收的参数组成的Promise,如果接收一个Promise,则直接返回它
type MyAwaited<T> = T extends MyPromise<infer R> ? MyAwaited<R> : T;
function isMyPromise<T>(value: unknown): value is MyPromise<T> {
return value instanceof MyPromise;
}
static resolve<V>(v: V): MyPromise<MyAwaited<V>> {
// 虽然构造函数中也会处理接收的Promise 但是返回值和原本的不是同一个 所以这里直接返回它
return isMyPromise<MyAwaited<V>>(v)
? v
: (new MyPromise<V>((resolve) => resolve(v)) as MyPromise<MyAwaited<V>>);
}
static reject(v: unknown): MyPromise<never> {
return isMyPromise(v) ? (v as MyPromise<never>) : new MyPromise((_, reject) => reject(v));
}
all
通过接收一个promise组成的数组,如果全部都resolve就返回结果组成的数组,如果有一个reject就失败
其实下面的静态方法都是差不多的,只有resolve reject的位置不同而已
static all<T extends readonly any[]>(promises: T): MyPromise<MyAwaited<T[number]>[]> {
return new MyPromise((resolve, reject) => {
const result: any[] = [];
for (const promise of promises) {
MyPromise.resolve(promise).then(
(fulfilled) => result.push(fulfilled),
(rejected) => reject(rejected)
);
if (result.length == promises.length) {
resolve(result); // resolve必须在循环内,不然会先执行打印的宏任务
}
}
});
}
数组中内置了异步迭代器的,所以如果要手动实现
Promise.all可以用for await ... of,像下面这样
function all(promises) {
return new Promise(async (resolve, reject) => {
const result = [];
try {
for await (const promise of promises) {
result.push(promise);
}
} catch (e) {
reject(e);
}
resolve(result);
});
}
any
any和all正好相反,只需要记录失败的数组,并且全部失败再reject
static any<T extends readonly any[]>(promises: T): MyPromise<MyAwaited<T[number]>> {
return new MyPromise((resolve, reject) => {
const failds: any[] = [];
for (const promise of promises) {
MyPromise.resolve(promise).then(resolve, (e) => {
failds.push(e);
if (failds.length == promises.length) {
reject(failds);
}
});
}
});
}
race
static race<T extends readonly any[]>(promises: T): MyPromise<MyAwaited<T[number]>> {
return new MyPromise((resolve, reject) => {
for (const promise of promises) {
MyPromise.resolve(promise).then(resolve, reject);
}
});
}
allSettled
static allSettled<T extends readonly any[]>(promises: T): MyPromise<Result<MyAwaited<T>>[]> {
return new MyPromise((resolve) => {
const result: Result<MyAwaited<T>>[] = [];
for (const promise of promises) {
Promise.resolve(promise).then(
(fulfilled) =>
result.push({ status: "fulfilled", value: fulfilled }) == promises.length
? resolve(result)
: null,
(e) =>
result.push({ status: "rejected", reason: e }) == promises.length
? resolve(result)
: null
);
}
});
}