性能优化:基于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

本库长期维护更新...

系列文章

相关推荐
崔庆才丨静觅5 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60615 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了6 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅6 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅6 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅6 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment6 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅7 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊7 小时前
jwt介绍
前端