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

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

地址:前端面试题库

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

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

一、场景介绍

  在离线的数据开发模块,用户可以在 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.mzph.cn/news/75630.shtml

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

相关文章

小白备战大厂算法笔试(六)——堆

文章目录 堆常用操作堆的实现存储与表示访问堆顶元素元素入堆元素出堆 常见应用建堆操作自上而下构建自下而上构建 TOP-K问题遍历选择排序堆 堆 堆是一种满足特定条件的完全二叉树&#xff0c;主要可分为下图所示的两种类型。 大顶堆&#xff1a;任意节点的值 ≥ 其子节点的值…

【Hello Algorithm】贪心算法

本篇博客介绍&#xff1a; 简单介绍下贪心算法 贪心算法 介绍贪心算法最小字典序的字符串拼接最多会议数切棍子的最小成本IPO灯塔问题 介绍贪心算法 贪心算法是一种极具有自然智慧的算法 它会使用以一种局部最功利的标准来做出一个当前看来最好的选择 如果说我们根据局部最优…

【docker】私有仓库搭建

Docker 私有仓库搭建 在 Docker 中&#xff0c;当我们执行 docker pull xxx 的时候 &#xff0c;它实际上是从 registry.hub.docker.com 这个地址去查找&#xff0c;这就是Docker公司为我们提供的公共仓库。在工作中&#xff0c;我们不可能把企业项目push到公有仓库进行管理。…

C高级 第四天

#!/bin/bash#1.实现一个对数组求和的函数,数组通过实参传递给函数 read -p "输入数组" -a arr sum0 function add() {for i in ${arr[*]}do((sumi))done } add arr echo $sum#2.写一个函数,输出当前用户的uid和gid,并使用变量接收结果 function my_id() {num1id -unu…

WebDAV之π-Disk派盘 + 小书匠

小书匠是一款功能丰富,强大的知识管理工具。全平台覆盖,离线数据存储,自定义数据服务器,所见即所得的 markdown 编辑体验。 小书匠提供了多种实用的编辑模式,例如:栏编辑、双栏编辑、三栏编辑、全屏写作、全屏阅读等。并且该软件还提供了许多有用的扩展语法,比如Latex公…

安装vCenter6.7出错 DNS服务原因

https://blog.csdn.net/seaship/article/details/107045006**&#xff21;.VCSA6.7的安装**第1步&#xff0c;运行VCSA 6.7部署程序&#xff0c;中间一些截图参考何老师相关部署文章即可第2步&#xff0c;配置VCSA虚拟机网络&#xff0c;FQDN填写虚拟机分配的IP地址&#xff0c…

SQLite3 操作命令以及c/c++编程API和例子

文章目录 数据库系统SQLiteSQLite数据类型SQLite语句介绍表的创建和查看表的删除插入和修改表数据的删除复制表和修改表结构事务处理函数操作数据分组group by约束联结表视图view触发器trigger日志操作索引 index SQLite c/c编程sqlite3_opensqlite3_closesqlite3_execsqlite3_…

机器人抓取检测技术的研究现状

1.分析法 图 1 为分析法在进行抓取检测时所采用的 一般策略[3] [3]Sahbani A, El-Khoury S, Bidaud P. An overview of 3D object grasp synthesis algorithms[J]. Robotics and Autonomous Systems, 2012, 60(3): 326-336. 首先,基于环境限制和机械手与物体模 型进行抓取检…

2023.9.8 基于传输层协议 UDP 和 TCP 编写网络通信程序

目录 UDP 基于 UDP 编写网络通信程序 服务器代码 客户端代码 TCP 基于 TCP 编写网络通信程序 服务器代码 客户端代码 IDEA 打开 支持多客户端模式 UDP 特点&#xff1a; 无连接性&#xff1a;发送端和接收端不需要建立连接也可相互通信&#xff0c;且每个 UDP 数据包都…

行业追踪,2023-09-11

自动复盘 2023-09-11 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

JVM系列 运行时数据区

系列文章目录 第一章 运行区实验 文章目录 系列文章目录前言一、堆&#xff08;Heap&#xff09;1.1、新生代/Young区1.1.1、Eden区1.1.2、Survival区 1.2、年老代&#xff08;old区&#xff09; 二、虚拟机栈&#xff08;Stack&#xff09;2.1、栈顶缓存技术2.2、溢出2.3、栈…

82 # koa-bodyparser 中间件的使用以及实现

准备工作 安装依赖 npm init -y npm i koakoa 文档&#xff1a;https://koajs.cn/# koa 中不能用回调的方式来实现&#xff0c;因为 async 函数执行的时候不会等待回调完成 app.use(async (ctx, next) > {console.log(ctx.path, ctx.method);if (ctx.path "/login…

C高级day4

一、实现一个对数组求和的函数&#xff0c;数组通过实参传递给函数 二、写一个函数&#xff0c;输出当前用户的uid和gid&#xff0c;并使用变量接收结果 三、XMind思维导图

JavaScript学习--Day04

元字符 边界符&#xff1a; /^/&#xff1a;以什么开头 /$/&#xff1a;以什么结尾 量词&#xff1a; 预定义类&#xff1a;

vs2019配置sfml外部库出现的问题

问题描述 在debug模式下程序正常运行&#xff0c;在release模式中报错 解决 C/C Windows环境下 boost 安装使用教程 Debug 通常称为调试版本&#xff0c;它包含调试信息&#xff0c;并且不作任何优化&#xff0c;便于程序员调试程序。 Release&#xff1a;Release通常称为发…

基于springboot+vue的实验室耗材管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…

【业务功能篇105】 微服务-springcloud-springboot-电商订单模块--秒杀服务-定时任务【上篇】

秒杀服务 一、商品上架 秒杀活动的结构图 通过定时任务触发&#xff1a; 定时任务由spring提供&#xff0c;需要通过注解开启&#xff0c;这里通过定义一个配置类&#xff0c;注入spring,对其配置类进行相应的注解&#xff0c;当然也可以注解放在我们的服务启动类上cron表达…

Windows wsl2安装Ubuntu

wsl&#xff08;Windows Subsystem for Linux&#xff09;即适用于Windows的Linux子系统&#xff0c;是一个实现在Windows 10 / 11上运行原生Linux的技术。 wsl2 为其迭代版本&#xff0c;可以更好的在Windows上运行Linux子系统。 这里以 Windows 11 安装Ubuntu作为示例。 开启…

提升效率:PostgreSQL准确且快速的数据对比方法

作为一款强大而广受欢迎的开源关系型数据库管理系统&#xff0c;PostgreSQL 在数据库领域拥有显著的市场份额。其出色的可扩展性、稳定性使其成为众多企业和项目的首选数据库。而在很多场景下&#xff08;开发|生产环境同步、备份恢复验证、数据迁移、数据合并等&#xff09;&a…

“JSR303和拦截器在Java Web开发中的应用与实践“

目录 引言JSR303什么是JSR303?为什么要使用JSR303?常用注解快速入门JSR303 拦截器什么是拦截器拦截器与过滤器应用场景快速入门拦截器 总结 引言 在Java Web开发过程中&#xff0c;我们经常会遇到需要对输入数据进行验证和处理&#xff0c;同时需要对请求进行拦截与控制的需…