You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

数据处理时Loader组件无法正常显示的问题求助

问题原因

你遇到的核心问题是同步长任务阻塞了主线程:虽然把数据处理逻辑放进了useEffect,但里面嵌套的while循环是同步执行的,会一直霸占JS主线程,导致浏览器根本没时间渲染Loader组件——直到整个循环跑完,React才会更新DOM,这时候Loader刚显示就被关闭,看起来就像Loader没工作。

修复方案

把同步循环改成异步分步执行,给浏览器留出渲染时间。关键是用async/await配合setTimeout(或requestIdleCallback)拆分长任务,同时用useRef保存需要跨异步步骤修改的变量,避免闭包和重渲染导致的变量重置问题。

修改后的代码

import { useState, useEffect, useRef } from 'react';
// 导入你的其他组件(Modal、Box、CircularProgress等)

const Simulate = (props) => {
    const [isLoading, setIsLoading] = useState(true); 

    const steps = props.nodes[0].data.fields.arrivals;
    const duracionPrueba = props.nodes[0].data.fields.duracionPrueba;
    const stepsTime = arrivalSteps(steps, duracionPrueba);

    // 用useRef保存异步操作中需要修改的变量,避免闭包和重渲染问题
    const visitasRef = useRef([]);
    const llegadasRef = useRef({});
    const finalizadasRef = useRef({});
    const tiempoRespuestaRef = useRef({});
    const erroresRef = useRef({});
    const filtradosRef = useRef({});
    const concurrenciaRef = useRef({});
    const colaRef = useRef({});
    const segundoRef = useRef(1);

    const [nodesPath, tree] = NodesPath(props.edges);
    const leaves = useRef([]);
    findLeaves(tree, leaves.current);
    weightNodes(tree, props.nodes, props.edges);
    
    useEffect(() => {
        // 异步执行模拟逻辑
        const runSimulation = async () => {
            // 处理steps循环:替换forEach为for循环,支持异步等待
            for (let i = 0; i < steps.length; i++) {
                const step = steps[i];
                let timeStep = 1;
                while (timeStep <= stepsTime[i]) {
                    console.log(segundoRef.current);

                    // 调用原逻辑,改用ref的current属性访问变量
                    runPath(nodesPath, props.nodes, step, llegadasRef.current, visitasRef.current, finalizadasRef.current, tree);
                    checkFinish(visitasRef.current, leaves.current, erroresRef.current, filtradosRef.current, concurrenciaRef.current, colaRef.current, tiempoRespuestaRef.current, props.nodes);

                    segundoRef.current++;
                    timeStep++;

                    // 让出主线程,让浏览器渲染Loader
                    await new Promise(resolve => setTimeout(resolve, 0));
                }
            }

            // 处理剩余的visitas
            while (visitasRef.current.length > 0) {
                console.log(segundoRef.current);
                runPath(nodesPath, props.nodes, 0, llegadasRef.current, visitasRef.current, finalizadasRef.current, tree);
                checkFinish(visitasRef.current, leaves.current, erroresRef.current, filtradosRef.current, concurrenciaRef.current, colaRef.current, tiempoRespuestaRef.current, props.nodes);

                segundoRef.current++;

                // 再次让出主线程
                await new Promise(resolve => setTimeout(resolve, 0));
            }

            // 整理结果并回调
            const simulateResults = {
                duracionPrueba,
                tiempoTotal: segundoRef.current,
                tiempoRespuesta: tiempoRespuestaRef.current,
                concurrencia: concurrenciaRef.current,
                cola: colaRef.current,
                errores: erroresRef.current,
                filtrados: filtradosRef.current,
                llegadas: llegadasRef.current,
                finalizadas: finalizadasRef.current,
            };
            props.handleSimulate(simulateResults);
            setIsLoading(false);
        };

        runSimulation();
    }, [props.nodes, props.edges, steps, duracionPrueba, stepsTime, nodesPath, tree]);


    if (isLoading) {
        return(
            <Modal
                open={isLoading}
                aria-labelledby="modal-modal-title"
                aria-describedby="modal-modal-description"
            >
                <Box sx={{style}} className='loader'>
                    <CircularProgress style={{transform: 'translate(-50%, -50%)'}} />
                </Box>
            </Modal>
        )
    }

    // 这里返回组件的其他内容,原代码未提供,暂用null占位
    return null;
}

关键修改说明

  1. 用useRef保存变量:原代码中的visitas、llegadas等变量会在组件重渲染时重新初始化,用useRef可以保留它们的引用,避免异步操作中拿到旧值。
  2. 异步拆分长任务:每次循环后用await setTimeout(...)让出主线程,让浏览器有时间渲染Loader组件,解决阻塞问题。
  3. 替换forEach为for循环:forEach不支持异步等待,改用for循环可以确保每一步循环完成后再执行下一步。

可选优化

如果想更高效地利用浏览器空闲时间,可以把await new Promise(resolve => setTimeout(resolve, 0))替换为:

await new Promise(resolve => requestIdleCallback(resolve));

requestIdleCallback会在浏览器空闲时执行回调,不会影响页面响应,但要注意它的兼容性(IE不支持)。

内容的提问来源于stack exchange,提问作者Nicolas Jorquera Martinez

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 08:55:37