性能优化:基于Promise的状态共享

一图胜千文:

背景:

在实际的软件开发过程中,异步操作是非常常见的场景。这些异步操作涵盖了各种各样的情况,其中最常见的就是网络请求和客户端与服务器之间的交互。这些操作往往需要消耗一定的时间和系统资源,比如CPU计算时间、内存占用甚至网络带宽。如果在短时间内相继进行多次异步操作,特别是这些操作是重复的,那么就会形成不必要的资源浪费。此时,我们就需要对这些操作进行优化,以减少资源的浪费,并提高应用程序的性能

在JavaScript中,Promise是管理异步操作的重要工具。Promise不仅可以让异步操作的编写更加简洁,而且还支持链式调用,使得代码结构更加清晰。然而,当面临上述的短时间内大量重复的异步操作的时候,单纯的使用Promise仍然会导致资源的浪费。因为每一次Promise的调用都会生成一个全新的对象,而这些对象在执行完毕后,如果没有被其他变量引用,就会被垃圾回收机制销毁。这就带来了内存的频繁分配与回收,消耗了大量的资源。

解决方案

为了解决这个问题,我们可以考虑实现一种机制,能够在某个Promise对象处于pending状态时,保存这个对象的状态和结果,并在之后的方法调用中直接返回这个结果。这样,当我们在短时间内多次请求同一个资源时,只需要发起一次真正的异步请求,其他的请求都可以直接返回之前的结果。这就是Promise状态共享的概念

具体来说,我们可以通过闭包保存一个Promise对象的引用,并在该Promise对象的状态变为fulfilled或rejected之后,清除这个引用。这样,在每次调用Promise的方法时,都先检查这个引用是否存在,如果存在直接返回该Promise对象。如果不存在,就发起新的异步请求,并将新的Promise对象的引用保存起来。

代码实现

javascript 复制代码
/**
 * 当前任务没有完成时 多次调用会共享同一个promise实例状态
 * @param task 需要运行的异步任务
 * @returns 
 */
export const sharePromise = (task: () => Promise<any>) => {
    let _shareP: Promise<any> | null = null;
    return () => {
        if (!_shareP) {
            _shareP = new Promise((_res, _rej) => {
                task().then((res) => {
                    _res(res)
                }).catch((err) => {
                    _rej(err)
                }).finally(() => {
                    _shareP = null;
                })
            });
        }
        return _shareP;
    };
};

通过这种方式,我们可以将短时间内多次发起的相同异步请求合并为一个,大大减少了资源的消耗,提高了应用程序的性能。

同时,通过共享Promise的状态和结果,可以确保所有的请求都获得相同的数据,使得应用程序的行为更加稳定和可预测。

验证

使用setTimeout声明一个异步任务

javascript 复制代码
const t = () => {
    return new Promise((_res, _rej) => {
        console.log('执行任务')
        setTimeout(() => {
            _res(1)
        }, 1000)
    })
}

case 1

多次调用异步任务 等待结果返回

javascript 复制代码
const sp = sharePromise(t);
const p1 = sp()
p1.then(res => { console.log('p1 res: ', res) });

const p2 = sp()
p2.then(res => { console.log('p2 res: ', res) });

const p3 = sp();
p3.then(res => { console.log('p3 res: ', res) });

console.log('p1 === p2', p1 === p2);
console.log('p2 === p3', p2 === p3);

可以看到我们调用了三次异步任务 但是实际只执行了一次异步操作 返回的p1 p2 p3是相同的实例对象 三个异步的结果也被共享了。当第一次异步操作结束 promise状态发生变化后 三次调用都可以正确的获取返回值1。

case 2

javascript 复制代码
const sp = sharePromise(t);
const p1 = sp()
p1.then(res => { console.log('111111: ', res) });

let p2: Promise<any>;
let p3: Promise<any>;
setTimeout(() => {
    p2 = sp()
    p2.then(res => { console.log('222222: ', res) });
}, 500)

setTimeout(() => {
    p3 = sp()
    p3.then(res => { console.log('333333: ', res) });
}, 2000)


setTimeout(()=>{
    console.log('p1 === p2', p1 === p2);
    console.log('p2 === p3', p2 === p3);
}, 4000)

异步任务执行耗时1s

p1 立即执行

p2 500m后在执行 期望p2复用p1的结果

p3 2s后在执行 此时p1应该已经结束 p3重新发起一次异步任务 不会复用p1的结果

运行结果如图:

可以看到和我们期望的是一致的。异步任务执行了两次。 p1 和 p2 是一个实例 状态共享。 p3是新的异步请求。 都可以正确的拿到返回结果。

github源码

share-promise

更多基于promise的 优化js运行时的解决方案 run-time-opti

本库长期维护更新...

系列文章

相关推荐
支撑前端荣耀1 分钟前
从零实现前端监控告警系统:SMTP + Node.js + 个人邮箱 完整免费方案
前端·javascript·面试
重铸码农荣光5 分钟前
🎯 从零搭建一个 React Todo 应用:父子通信、状态管理与本地持久化全解析!
前端·react.js·架构
用户4099322502125 分钟前
Vue3 v-if与v-show:销毁还是隐藏,如何抉择?
前端·vue.js·后端
Mr_chiu6 分钟前
🚀 效率暴增!Vue.js开发必知的15个神级提效工具
前端
shanLion6 分钟前
Vite项目中process报红问题的深层原因与解决方案
前端·javascript
烟袅8 分钟前
从零构建一个待办事项应用:一次关于组件化与状态管理的深度思考
前端·javascript·react.js
前端小万11 分钟前
草稿
前端
闲云一鹤13 分钟前
将地图上的 poi 点位导出为 excel,并转换为 shp 文件
前端·cesium
岁月宁静1 小时前
MasterGo AI 实战教程:10分钟生成网页设计图(附案例演示)
前端·aigc·视觉设计
狗头大军之江苏分军1 小时前
快手12·22事故原因的合理猜测
前端·后端