JS 性能优化之大数据切片处理

414 阅读1分钟

在业务逻辑中,前端常常会碰到后端扔给你一大坨没有经过处理的数据,前端处理起来费时费力不说还得考虑性能的影响

这里针对我业务中碰到的报表导出时,大量数据循环处理导致页面渲染卡死。所写的一个通用的切片处理方法,需要的可以自己去进行改造

已验证 15000 条数据导出逻辑

已实现效果

  1. 可返回数据处理百分比
  2. 页面卡顿情况完美解决
  3. 导出同理可以换成其他数据处理的方法
/**
 * 切片处理大量数据
 * 
 * 问题背景:
 * 1、一个 5000+ 条数据的数组进行处理
 * 2、对这个数据进行切片处理
 * 
 * 问题解决
 * 1、分为两部分
 * 
 * 例子: excel.js 的 exportCsvWithLargeData 方法 
 *          - 传入数组,里面是固定处理的方法
 *       super_task 类方法
 */

function _runTask2(callback){
    let start = Date.now()

    requestAnimationFrame(()=>{
        //时间差小于16.6毫秒,执行 16.6 毫秒为一帧渲染的时机
        if(Date.now() - start < 16.6){
            callback(true)
        }else{
            //不合适的话,我们就要重新递归调用这个_runTask函数
            _runTask2(callback)
        }
    })
}

const arr1 = Array.from({length: 10}).map((_, num) => (num + 1) * 10)

export const handleLargeData = async (size = 50, data = [], task = () => {}, onProcess = () => {}) => {
    try {
        // console.time('handle')
        if(data.length === 0) {
            onProcess(100) // 空数据直接百分百
            return []
            // throw 'No Data'
        }

        // 分片
        const step = Math.ceil(data.length / size)

        // 数据缓存
        let cacheData = []

        // 遍历分片
        for (let i = 0; i < step; i++) {
            await new Promise(async (resolve, reject) => {
                try {
                    // 执行 task 方法
                    const formatData = task(data.slice(i*size, (i+1)*size))

                    cacheData = cacheData.concat(formatData)
                    
                    // 统计进度
                    const process = (i / step) * 100;
                    if(arr1.includes(Number(process.toFixed()))) {
                        onProcess(process)
                    }

                    // 适当暂停,避免页面无法执行渲染
                    await new Promise((_resolve) => {
                        // setTimeout(() => _resolve(true), 50);
                        _runTask2(_resolve)
                    });

                    resolve(true);
                    
                } catch (error) {
                    reject(error)
                }
            })
        }

        // 遍历完成时,固定进度为 100%
        onProcess(100);
        // console.log(cacheData)
        // console.timeEnd('handle')

        return cacheData

    } catch (error) {
        console.warn(error)
    }
}