前端開發進階:前端開發中如何高效渲染大數據量?

来源:https://www.cnblogs.com/DTinsight/archive/2023/08/23/17651637.html
-Advertisement-
Play Games

在日常工作中,有時會遇到一次性往頁面中插入大量數據的場景,在數棧的[離線開發](https://www.dtstack.com/dtinsight/batchworks?src=szsm)(以下簡稱離線)產品中,就有類似的場景。本文將通過分享一個實際場景中的[前端開發](https://www.dt ...


在日常工作中,有時會遇到一次性往頁面中插入大量數據的場景,在數棧的離線開發(以下簡稱離線)產品中,就有類似的場景。本文將通過分享一個實際場景中的前端開發思路,介紹當遇到大量數據時,如何實現高效的數據渲染,以達到提升頁面性能和用戶體驗的目的。

渲染大數據量時遇到的問題

在離線的數據開發模塊,用戶可以在 SQL 編輯器中編寫 SQL,再通過整段運行/分段運行來執行 SQL。在點擊整段運行後,從運行成功日誌列印後到展示結果的過程中,有一段時間頁面會很卡頓,主要表現為編輯器編寫卡頓。

file

我們是在解決 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 請求完成後,集中進行渲染。此時,就會出現一次性往頁面中插入大量數據的場景,導致卡頓。那麼,我們怎麼解決上述問題呢?

解決思路

可以看出,上述邏輯主要有兩個問題:大批量請求 selectData 介面和集中性數據渲染。我們通過如下所示的解決思路去處理這些問題。

任務分組

依舊通過 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);
});

請求分組 + 任務分組

問題中的大批量請求 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);

請求分組

上一種方案的代碼相對來說又些難以理解,屬於上午寫,下午忘的邏輯,註釋也不好寫,不利於維護。基於實際情況,我們嘗試下僅對請求作分組處理,看看效果。

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

解決思路解析

· 解決大數據量渲染的問題,常見方法有:時間分片虛擬列表

· 解決同步阻塞的問題,常見方法有:任務分解、非同步等

· 如果某個任務執行時間較長的話,從優化的角度,我們通常會考慮將該任務分解成一系列的子任務

在任務分組一節,我們將 setTimeout 的時間間隔設置為 100ms,也就是我認為最快在 100ms 內能完成渲染。但假設不到 100ms 就完成了渲染,那麼就需要白白等待一段時間,這是沒有必要的,這時可以考慮 window.requestAnimationFrame 方法。

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

第三節的請求分組,實際上已經達到了渲染任務分組的效果。本文更多的是提供一個解決思路,上述方式也是基於對時間分片的理解實踐。

在軟體開發中,性能優化是一個重要的方面,但並不是唯一追求,往往還需要考慮多個因素,包括功能需求、可維護性、安全性等等。根據具體情況,綜合使用多種技術和策略,找到最佳的解決方案,才是最終目的。

《數棧產品白皮書》:https://www.dtstack.com/resources/1004?src=szsm

《數據治理行業實踐白皮書》下載地址:https://www.dtstack.com/resources/1001?src=szsm

想瞭解或咨詢更多有關袋鼠雲大數據產品、行業解決方案、客戶案例的朋友,瀏覽袋鼠雲官網:https://www.dtstack.com/?src=szbky

同時,歡迎對大數據開源項目有興趣的同學加入「袋鼠雲開源框架釘釘技術qun」,交流最新開源技術信息,qun號碼:30537511,項目地址:https://github.com/DTStack


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ## 一、為什麼要使用 LINQ 要理解為什麼使用 LINQ,先來看下下麵的例子 例子:要統計字元串中每個字母出現的頻率(忽略大小寫),然後按照從高到低的順序輸出出現頻率高於2次和其出現的的頻率。如果用傳統的 Sql 語句來寫,一定是非常的繁雜,如果用 LINQ 語句來寫,效果如下 ```c# st ...
  • [toc] # Linux運維工程師面試題(1) > 祝各位小伙伴們早日找到自己心儀的工作。 > 持續學習才不會被淘汰。 > 地球不爆炸,我們不放假。 > 機會總是留給有有準備的人的。 > 加油,打工人! ## 1 別名、內部命令、外部命令的執行順序 命令執行尋找順序:別名 > 內部命令 > 外部命 ...
  • [toc] ### 1.文件操作 #### 1.1 創建文件 ``` shell # touch+文件名 # 例子: # 創建一個文件 touch hello.c # 創建多個文件 touch hello.c hi.c ``` #### 1.2 刪除文件 ``` shell # rm+文件名 # 例 ...
  • 因為我用的是windows伺服器,因此需要一臺虛擬機,用來安裝centos,虛擬機的安裝網上好多教程,這裡不做過多介紹 這次同樣是按步操作 在本地伺服器創建下載目錄 -> 將yum文件下載到本地 -> 在遠程伺服器上創建目錄 -> 上傳文件到遠程伺服器目錄 -> 使用命令安裝yum到伺服器上 這次的 ...
  • # 任務與協程 ## 區別 > 一個程式可以只有任務、只有協程、二者都有,但不可以通過隊列/信號量互相傳遞數據 ## 任務特點 1. 任務之間可以互相獨立 2. 每個任務分配自己的堆棧,提高了RAM使用率 3. 操作簡單、按優先順序搶占式執行 4. 搶占容易導致重入(執行任務時被其他線程或進程調用了) ...
  • ![](https://img2023.cnblogs.com/blog/3076680/202308/3076680-20230822115524099-438612716.png) # 1. 在執行語句之前,會先檢查下列事項 ## 1.1. 是否有許可權執行該語句 ## 1.2. 是否有許可權訪問指 ...
  • 排序查詢:select 欄位列表 from [表名] order by [欄位名1] [asc升序/desc降序,預設值為升序],[欄位名2] [排序方式];//欄位名1為優先順序排序,如果欄位名1有相同的,再以欄位名2排序 聚合函數: count 統計數量(一般不選null的列) max 最大值 m ...
  • ## 1、字元集概述 - Oracle語言環境的描述包括三部分:language、territory、characterset(語言、地域、字元集) - language:主要指定伺服器消息的語言,提示信息顯示中文還是英文 - territory:主要指定伺服器的數字和日期的格式 - charact ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...