js三座大山
一:函数式编程
js三座大山之函数1
js三座大山之函数2-作用域与动态this
二:面向对象编程
js三座大山之对象,继承,类,原型链
三:异步编程:
js三座大山之异步一单线程,event loope,宏任务&微任务
js三座大山之异步二异步方案
js三座大山之异步三promise本质
js三座大山之异步四-Promise的同步调用消除异步的传染性
js三座大山之异步五基于异步的js性能优化
js三座大山之异步六实现微任务的N种方式
js三座大山之异步七实现宏任务的N种方式
异步的传染性
正常情况我们使用异步的async/await + promise运行代码时是这样的。
const test3 = async () => {
const res = await new Promise((resolve) => {
setTimeout(() => {
resolve(3);
}, 2000);
});
console.log("test 3 res", res);
return res;
};
const test2 = async () => {
return test3();
};
const test1 = async () => {
return test2();
};
const main = async () => {
const res = await test1();
console.log("test 最终结果 res", res);
};
main();
如上面的代码,我们在执行 test3 函数时返回了一个 promise 的执行结果,为了拿到promise异步回到的结果,我们必须加上 async 和 await 去等待结果的反馈。 这就导致了如果要使用 async 和 await 语法糖必须在函数使用的链路上全部都要使用 async 和 await,这就是所谓的异步传染性。看起来就很丑,有木有!
消除异步的传染性
思考下能否同步的运行promsie获取结果,消除掉async/await呢?
- 答案: 因为 js 是单线程,如果想要获取异步代码并且执行效果还得是同步的话可以利用
报错 + 缓存 + event-loop
。
- 在test3中
调用异步api,将当前promise实例作为异常内容抛出,同时外层捕获这个错误
,因为是一个promise所以在上面注册callback函数
,等待后续执行,然后退出函数。第一次调用执行结束。 - 异步api执行完成后加入
task queue
中 - 执行栈从队列中取出任务,运行任务,调用之前注册的
callback函数
,callback里面先缓存结果然后再次运行main方法. - 再次走到test3时,直接从缓存里面同步取结果返回即可。
封装KillAwait对象
实现了KillAwait对象支持多个异步promise的调用 同时消除了异步的传染性,使用该对象有几个条件需要注意:
- 内部的函数必须是纯函数,不能有副作用。`因为每遇到一个异步代码就需要暂停然后等待结果再次从头执行,所以函数都运行多次。
- 需要使用KillAwait.promise来生成promise对象
- 在外层通过KillAwait.execute来执行方法。
测试代码:
import { KillAwait } from "dynamic-tasks
const test3 = () => {
const res = KillAwait.promise((resolve) => {
setTimeout(() => {
resolve(3);
}, 2000);
});
console.log("test 3 res", res);
return res;
};
const test2 = () => {
const res = KillAwait.promise((resolve) => {
setTimeout(() => {
resolve(2);
}, 2000);
});
console.log("test 2 res", res);
return test3();
};
const test1 = () => {
const res1 = KillAwait.promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 2000);
});
console.log("test 1 res1", res1);
const res2 = KillAwait.promise((resolve) => {
setTimeout(() => {
resolve(1.2);
}, 2000);
});
console.log("test 1 res2", res2);
return test2();
};
const main2 = () => {
const res = test1();
console.log("test 最终结果 res", res);
};
KillAwait.execute(main2);
// res
test 1 res1 1
test 1 res1 1
test 1 res2 1.2
test 1 res1 1
test 1 res2 1.2
test 2 res 2
test 1 res1 1
test 1 res2 1.2
test 2 res 2
test 3 res 3
test 最终结果 res 3
KillAwaitPromise
import { isPromiseLike } from "./promise-like";
let curCount = 0;
let map = new Map();
const promise = (callback: () => void) => {
if (map.get(curCount)) {
const res = map.get(curCount);
curCount++;
return res;
}
const p = new Promise(callback);
throw p;
};
const run = (action: () => void) => {
try {
action();
} catch (error) {
if (!isPromiseLike(error)) {
throw error;
}
(error as any)
.then((res: any) => {
map.set(curCount, res);
})
.catch((err: any) => {
map.set(curCount, err);
})
.finally(() => {
curCount = 0;
run(action);
});
}
};
const execute = (task: () => void) => {
curCount = 0;
map = new Map();
run(task);
};
export const KillAwait = {
execute,
promise,
};
export default KillAwait;
github仓库
源码 kill-await
更多基于promise的 优化js运行时的解决方案 run-time-opti
本库长期维护更新...