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

本库长期维护更新...

系列文章

相关推荐
胡西风_foxww13 分钟前
【ES6复习笔记】数值扩展(16)
前端·笔记·es6·扩展·数值
mosen86815 分钟前
uniapp中uni.scss如何引入页面内或生效
前端·uni-app·scss
白云~️15 分钟前
uniappX 移动端单行/多行文字隐藏显示省略号
开发语言·前端·javascript
沙尘暴炒饭18 分钟前
uniapp 前端解决精度丢失的问题 (后端返回分布式id)
前端·uni-app
昙鱼32 分钟前
springboot创建web项目
java·前端·spring boot·后端·spring·maven
天天进步201537 分钟前
Vue项目重构实践:如何构建可维护的企业级应用
前端·vue.js·重构
小华同学ai40 分钟前
vue-office:Star 4.2k,款支持多种Office文件预览的Vue组件库,一站式Office文件预览方案,真心不错
前端·javascript·vue.js·开源·github·office
APP 肖提莫42 分钟前
MyBatis-Plus分页拦截器,源码的重构(重构total总数的计算逻辑)
java·前端·算法
问道飞鱼1 小时前
【前端知识】强大的js动画组件anime.js
开发语言·前端·javascript·anime.js
k09331 小时前
vue中proxy代理配置(测试一)
前端·javascript·vue.js