如何按不同时间间隔、不同参数顺序调用async函数实现进度条递增
问题原因
之前的实现出现进度异常、卡顿冻结主要是以下几点导致:
- 定时器未做清理:组件卸载后仍然触发状态更新,造成内存泄漏,大量无效定时器累积就会引发卡顿
- 请求并发无顺序控制:多个定时任务重叠触发接口请求,返回顺序不可控会导致进度条回退
- 副作用依赖缺失:useEffect依赖为空,外部传入的订单状态变化时无法同步更新进度
正确实现方案
完整修正代码
import { useState, useEffect } from 'react'; import moment from 'moment'; // 阶段定义保持不变 const STAGES = { PENDING: 'Pending Order Confirmation', CONFIRMED: 'Order Confirmed', PICKING_UP: 'Picking Up', EXAMINING: 'Examining', SANITIZING: 'Sanitizing', RECYCLING: 'Recycling', }; const progression = { [STAGES.PENDING]: 5, [STAGES.CONFIRMED]: 20, [STAGES.PICKING_UP]: 40, [STAGES.EXAMINING]: 60, [STAGES.SANITIZING]: 70, [STAGES.RECYCLING]: 100, }; // 新增:阶段流转顺序 const STAGE_ORDER = [ STAGES.PENDING, STAGES.CONFIRMED, STAGES.PICKING_UP, STAGES.EXAMINING, STAGES.SANITIZING, STAGES.RECYCLING ]; // 新增:每个阶段对应的距离订单创建时间的间隔秒数,可根据需求调整 const STAGE_DELAY_SECONDS = { [STAGES.PENDING]: 3, // 订单创建3秒后进入已确认 [STAGES.CONFIRMED]: 10, // 订单创建10秒后进入取件中 [STAGES.PICKING_UP]: 20, // 订单创建20秒后进入检测中 [STAGES.EXAMINING]: 30, // 订单创建30秒后进入消毒中 [STAGES.SANITIZING]: 45, // 订单创建45秒后进入回收完成 }; const ProgressBar = ({ order }) => { const { status, order_date } = order; const msTillXSeconds = (seconds) => { const targetTime = new Date(moment(order_date).add(seconds, 'seconds')).getTime(); const diff = targetTime - Date.now(); // 兜底:如果已经过了目标时间,直接返回0立即执行 return diff > 0 ? diff : 0; }; const [progress, setProgress] = useState(progression[status]); const [internalStatus, setInternalStatus] = useState(status); const updateStatus = async (newStatus) => { try { const body = { status: newStatus }; const response = await fetch(`http://localhost:5000/orders/${order.order_id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', token: localStorage.token }, body: JSON.stringify(body), }); if (response.ok) { setProgress(progression[newStatus]); setInternalStatus(newStatus); } } catch (err) { console.error(err.message); } }; useEffect(() => { // 已经到最终阶段,停止调度 if (internalStatus === STAGES.RECYCLING) return; const currentStageIndex = STAGE_ORDER.indexOf(internalStatus); // 阶段不存在的异常处理 if (currentStageIndex === -1 || currentStageIndex >= STAGE_ORDER.length - 1) return; const nextStage = STAGE_ORDER[currentStageIndex + 1]; const delayMs = msTillXSeconds(STAGE_DELAY_SECONDS[internalStatus]); // 设置定时器,延迟到指定时间后更新到下一个阶段 const timerId = setTimeout(async () => { await updateStatus(nextStage); }, delayMs); // 清理函数:组件卸载/依赖变化时清空定时器,避免无效执行 return () => clearTimeout(timerId); }, [internalStatus, order_date]); // 同步外部传入的status useEffect(() => { setProgress(progression[status]); setInternalStatus(status); }, [status]); return ( <> <div className="progress"> <div className="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" style={{ width: `${progress}%` }} > </div> </div> <p className="text-center my-1">{internalStatus}</p> </> ); };
核心优化点
- 明确了阶段流转的顺序,不会出现跳阶段的异常
- 所有定时器都有对应的清理逻辑,组件卸载时会自动清空未执行的任务,彻底解决内存泄漏和卡顿问题
- 采用串行执行逻辑,前一个阶段的接口请求完成后才会调度下一个阶段,避免请求乱序导致的进度回退
- 补充了副作用依赖,外部传入的订单状态变化时会自动同步内部进度
- 对延迟时间做了兜底处理,订单创建时间过久时也不会出现异常
内容的提问来源于stack exchange,提问作者mysl
相关产品推荐
相关产品推荐

