【面试题】前端开发中如何高效渲染大数据量?

前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

【国庆头像】- 国庆爱国 程序员头像!总有一款适合你!

在日常工作中,较少的能遇到一次性往页面中插入大量数据的场景,数栈的离线开发(以下简称离线)产品中,就有类似的场景。本文将分享一个实际场景中的前端开发思路,实现高效的数据渲染,提升页面性能和用户体验。

一、场景介绍

  在离线的数据开发模块,用户可以在 sql 编辑器中编写 sql,再通过 整段运行/分段运行 来执行 sql。在点击 整段运行 后,运行成功日志打印后到展示结果的过程中,有一段时间页面很卡顿,主要表现为编辑器编写卡顿。

二、性能问题

  我们是在解决 sql 最大运行行数 问题时,发现了上述需要进行性能优化的场景。 先来梳理下当前代码的设计逻辑: 

file

  • 前端将选中的 sql 传递给服务端,服务端返回一个调度运行的 jobId;
  • 前端接着以该 jobId 轮询服务端,查询任务的执行状态;
  • 当轮询到任务已完成时,选中的 sql 中如果有查询语句,服务端则会按 select 语句的顺序返回一个 sqlId 的数组集合;
  • 前端基于 n 个 sqlId 的集合,并发 n 个 selectData 的请求;
  • 所有的 selectData 请求完成后渲染数据;

为了保证结果最终的展示顺序和 select 语句顺序一致,我们为单纯的 sqlIdList 循环方法加上了 Promise.allsettled 的方法,使得 n 个 selectData 的请求顺序和 select 语句顺序一致。

file

  由上述逻辑可以看出,问题可能出现在如果选中的 sql 中有大量 select 语句的话,会在「整段运行」完成后大批量请求 selectData 接口,再等待所有 selectData 请求完成后,集中进行渲染。此时,就会出现一次性往页面中插入大量数据的场景。那么,我们怎么解决上述问题呢?

三、解决思路

  可以看出,上述逻辑主要有两个问题:

  • 1、大批量请求 selectData 接口;
  • 2、集中性数据渲染。

1、任务分组

  依旧通过 Promise.allsettled 拿到所有 selectData 接口返回的结果,将原先集中渲染看作是一个大任务,我们将任务拆分成单个的 selectData 结果渲染任务;再根据实际情况,对单个任务进行分组,比如两个一组,渲染完一组再渲染下一组。 拆分完任务,就涉及到了任务的优先级问题,优先级决定了哪个任务先执行。这里采用最原始的“抢占式轮转”,按 sqlIdList 的顺序保留编辑器中的 sql 顺序。

Promise.allSettled(promiseList).then((results = []) => {const renderOnce = 2; // 每组渲染的结果 tab 数量const loop = (idx) => {if (promiseList.length <= idx) return;results.slice(idx, idx + renderOnce).forEach((item, idx) => {if (item.status === 'fulfilled') {handleResultData(item?.value || {}, sqlIdList[idx]?.sqlId);} else {console.error('selectExecResultDataList Promise.allSettled rejected',item.reason);}});setTimeout(() => {loop(idx + renderOnce);}, 100);};loop(0);
});

2、请求分组 + 任务分组

  问题1 中的大批量请求 selectData 接口,也是一个突破点。我们可以将请求进行分组,每次以固定数量的 sqlId 去请求 selectData 接口,比如每组请求 6 个 sqlId 的结果,当前组的请求全部结束后再进行渲染;为了保证效果最优,这里也引入任务分组的思路。

const requestOnce = 6; // 每组请求的数量
// 将一维数组转换成二维数组
const sqlIdList2D = convertTo2DArray(sqlIdList, requestOnce);
const idx2D = 0; // sqlIdList2D 的索引const requestLoop = (index) => {if (!sqlIdList2D[index]) return;const promiseList = sqlIdList2D[index].map((item) =>selectExecResultData(item?.sqlId));Promise.allSettled(promiseList).then((results = []) => {const renderOnce = 2; // 每组渲染的结果 tab 数量const loop = (idx) => {if (promiseList.length <= idx) return;results.slice(idx, idx + renderOnce).forEach((item, idx) => {if (item.status === 'fulfilled') {handleResultData(item?.value || {}, sqlIdList[idx]?.sqlId);} else {console.error('selectExecResultDataList Promise.allSettled rejected',item.reason);}});setTimeout(() => {loop(idx + renderOnce);}, 100);};loop(0);}).finally(() => {requestLoop(index + 1);});
};
requestLoop(idx2D);

3、请求分组

  上一种方案的代码写出来太难以理解了,属于上午写,下午忘的逻辑,注释也不好写,不利于维护。基于实际情况,我们尝试下仅对请求作分组处理,看看效果。

const requestOnce = 3; // 每组请求的数量
// 将一维数组转换成二维数组
const sqlIdList2D = convertTo2DArray(sqlIdList, requestOnce);
const idx2D = 0; // sqlIdList2D 的索引const requestLoop = (index) => {if (!sqlIdList2D[index]) return;const promiseList = sqlIdList2D[index].map((item) =>selectExecResultData(item?.sqlId));Promise.allSettled(promiseList).then((results = []) => {results.forEach((item, idx) => {if (item.status === 'fulfilled') {handleResultData(item?.value || {}, sqlIdList[idx]?.sqlId);} else {console.error('selectExecResultDataList Promise.allSettled rejected',item.reason);}});}).finally(() => {requestLoop(index + 1);});
};
requestLoop(idx2D);

file

四、思路理解

1、解决大数据量渲染的问题,常见方法有:时间分片、虚拟列表等;
2、解决同步阻塞的问题,常见方法有:任务分解、异步等;
3、如果某个任务执行时间较长的话,从优化的角度,我们通常会考虑将该任务分解成一系列的子任务。

  在任务分组一节,我们将 setTimeout 的时间间隔设置为 100ms,也就是我认为最快在 100ms 内能完成渲染;但假设不到 100ms 就完成了渲染,那么就需要白白等待一段时间,这是没有必要的。这时可以考虑window.requestAnimationFrame 方法。

- setTimeout(() => {
+ window.requestAnimationFrame(() => {loop(idx + renderOnce);
- }, 100);
+ });

  第三节的请求分组,实际上达到了渲染任务分组的效果。本文更多的是提供一个解决思路,上述方式也是基于对时间分片的理解实践。

五、写在最后

  在软件开发中,性能优化是一个重要的方面,但并不是唯一追求,往往还需要考虑多个因素,包括功能需求、可维护性、安全性等等。根据具体情况,综合使用多种技术和策略,以找到最佳的解决方案。

前端面试题库 (面试必备)            推荐:★★★★★

地址:前端面试题库

【国庆头像】- 国庆爱国 程序员头像!总有一款适合你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/132465.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

易点易动固定资产管理系统:助力事业单位实现固定资产智能化管理

在日常运营中&#xff0c;事业单位面临着大量固定资产的管理挑战。为了提高资产利用率、降低运营成本&#xff0c;并确保资产安全与准确的账务管理&#xff0c;事业单位亟需一款强大而智能的固定资产管理系统。易点易动固定资产管理系统应运而生&#xff0c;为事业单位提供了一…

vue网页缓存页面与不缓存页面处理

在主路由页面 <template><div style"height: 100%"><!-- 缓存 --><keep-alive><router-view v-if"$route.meta.keepAlive"></router-view></keep-alive><!-- 不缓存 --><router-view v-if"!$rou…

ChatGPT 和 Elasticsearch:APM 工具、性能和成本分析

作者&#xff1a;LUCA WINTERGERST 在本博客中&#xff0c;我们将测试一个使用 OpenAI 的 Python 应用程序并分析其性能以及运行该应用程序的成本。 使用从应用程序收集的数据&#xff0c;我们还将展示如何将 LLMs 成到你的应用程序中。 在之前的博客文章中&#xff0c;我们构建…

Can‘t load the model for ‘stabilityai/sd-vae-ft-mse‘

Can’t load the model for ‘stabilityai/sd-vae-ft-mse’. If you were trying to load it from ‘https://huggingface.co/models’, make sure you don’t have a local directory with the same name. Otherwise, make sure ‘stabilityai/sd-vae-ft-mse’ is the correct…

iOS pod repo push 报错 ld: file not found: libarclite_iphoneos.a 问题解决方案

背景 Xcode 升级 14.3 之后&#xff0c;在Xcode 运行项目会收到以下错误 File not found: /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/arc/libarclite_iphoneos.a 项目中可以通过以下方法解决编译错误&#xff0c;就是在 …

铝及铝合金产品标识知识学习记录

声明 本文是学习GB-T 42916-2023 铝及铝合金产品标识. 而整理的学习笔记,分享出来希望更多人受益,如果存在侵权请及时联系我们 1— 圆铸锭表面&#xff1b; 2——切完头尾的圆铸锭尾端(引锭头端)。 图 9 圆铸锭刻痕标识示意图(一) 示 例 2 : 5A06 牌号、铸态、尺寸规格为…

uniapp微信小程序《隐私保护协议》弹窗处理流程

背景 《关于小程序隐私保护指引设置的公告》 《小程序隐私协议开发指南》 流程 1.第一步 必须设置且审核通过&#xff01;&#xff01;&#xff01; 2.第二步 uniapp在manifest.json中添加&#xff01;&#xff01;&#xff01; /* 在 2023年9月15号之前&#xff0c;在 ap…

景联文科技可为多模态语音翻译模型提供数据采集支持

8月22日Facebook的母公司Meta Platforms发布了一种能够翻译和转录数十种语言的人工智能模型——SeamlessM4T&#xff0c;可以在日常生活中或者商务交流中为用户提供更便捷的翻译和转录服务。 相较于传统的文本翻译&#xff0c;这项技术的最大区别在于它可以实现端到端的语音翻译…

4.4-Spring源码循环依赖终极讲解

回顾上期内容 new 容器 new AnnotateBeanDefinitionReader 的时候创建很多创世纪的类&#xff0c;其中有一个ConfigurationPostProcessor是用来解析配置类的&#xff0c;将其注册起来存到Bean定义的Map中【这个类是基于Bean工厂后置处理器的】 这一步是将配置类注册到Bean定…

C++之编译时预定义宏flag(二百一十二)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

按图搜索淘宝商品(拍立淘)API接口 搜爆款商品 图片搜索功能api 调用示例

接口名称&#xff1a;item_search_img 公共参数 请求地址: 测试item_search_img 名称类型必须描述keyString是调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_s…

Linux 下spi设备驱动

参考&#xff1a; Linux kernel 有关 spi 设备树参数解析 Linux kernel 有关 spi 设备树参数解析 - 走看看 Linux SPI驱动框架(1)——核心层 Linux SPI驱动框架(1)——核心层_linux spi驱动模型_绍兴小贵宁的博客-CSDN博客 Linux SPI驱动框架(2)——控制器驱动层 Linux SPI驱…

Redis过期时间的思考

当我们把 Redis 当做缓存来使用时&#xff0c;设置过期时间是必须的&#xff0c;但具体设置多少的过期时间呢&#xff0c;针对不同的场景会有不同的决策。 虚假一个场景&#xff0c;我们基于用户的地理位置推荐附近的陌生主播&#xff0c;用户可以线下去找主播沟通。当系统第一…

【PickerView案例08-国旗搭建界面加载数据 Objective-C预言】

一、来看我们第三个案例 1.来看我们第三个关于PickerView的一个案例, 首先呢,我要问大家一下, 咱们这个是几组数据呢, 这是一个pickerView,只不过,它显示的是什么,一个界面, 前面两个案例,都是文字 这个案例,开始有图片了, 总结一下这三个案例: 1)第一个案例…

R3LIVE源码解析(10) — R3LIVE中r3live_vio.cpp文件

目录 1 r3live_vio.cpp简介 2 r3live_vio.cpp源码解析 1 r3live_vio.cpp简介 R3LIVE主要的公式推导在VIO上&#xff0c;所以我们来细细的分析这部分的功能。R3LIVE将VIO分成了两步&#xff0c;一是直接通过帧间的光流来追踪地图点&#xff0c;并且通过最小化追踪到的地图点的…

C++之打印编译全过程(二百一十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

华为星闪联盟:引领无线通信技术创新的先锋

星闪&#xff08;NearLink&#xff09;&#xff0c;是由华为倡导并发起的新一代无线短距通信技术&#xff0c;它从零到一全新设计&#xff0c;是为了满足万物互联时代个性化、多样化的极致、创新体验需求而诞生的。这项技术汇聚了中国300多家头部企业和机构的集体智慧&#xff…

Python工程师Java之路(p)Module和Package

文章目录 1、Python的Module和Package2、Java的Module和Package2.1、Module2.1.1、分模块开发意义2.1.2、模块的调用 2.2、Package Module通常译作模块&#xff0c;Package通常译作包 1、Python的Module和Package Python模块&#xff08;Module&#xff09;&#xff1a;1个以.…

主从模式详解

主从模式是分布式系统中&#xff0c;多个服务器部署redis的一种方式。分布式系统主要是解决“单点问题”。 如果某个服务器程序&#xff0c;只有一个节点&#xff08;只有一个物理服务器&#xff0c;来部署服务器程序&#xff09;。存在可用性问题&#xff0c;如果这个机器挂了…

算法分析与设计编程题 回溯法

装载问题 题目描述 解题代码 递归回溯 // goods[i]表示货物i的重量, c1,c2分别表示货船1和货船2的载重量 vector<vector<int>> optimalLoading(vector<int>& goods, int c1, int c2) {int n goods.size(); // 货物数量int maxSum 0; // 当前最大载货…