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

React中Promise.all().then()先于单个Promise的setState渲染触发的问题

问题分析

核心问题是你当前的Promise.all只等待了fetch请求完成,但每个请求的后续处理(比如setTimeout模拟的耗时逻辑)并没有被纳入Promise.all的等待队列。所以当所有fetch请求返回后,Promise.all.then就会立刻执行,设置done状态,但此时单个请求的then里的setState可能还在排队执行,导致进度条提前隐藏,数据还没完全更新。

解决方案

把每个请求的完整处理流程(包括fetch、后续逻辑、setState)都封装成一个独立的Promise,让Promise.all等待所有完整流程结束后再执行状态更新。

修正后的代码示例

import { useState, useEffect } from 'react';

function ResourceTable() {
  const [data, setData] = useState({});
  const [done, setDone] = useState(false);
  const totalRequests = 20;

  useEffect(() => {
    // 封装每个请求的完整流程为Promise
    const requestPromises = Array.from({ length: totalRequests }, (_, index) => {
      return new Promise((resolve) => {
        // 模拟fetch请求
        fetch(`https://api.example.com/resource/${index}`)
          .then(res => res.json())
          .then(rawData => {
            // 模拟耗时处理逻辑
            setTimeout(() => {
              const processedData = { [index]: rawData };
              // 更新数据
              setData(prev => ({ ...prev, ...processedData }));
              console.log(`Render ${Object.keys(data).length + 1}`);
              resolve(); // 只有当整个流程完成后才resolve
            }, Math.random() * 1000);
          });
      });
    });

    // 等待所有完整流程结束
    Promise.all(requestPromises).then(() => {
      console.log(`Promise.all.then: ${Object.keys(data).length}`);
      setDone(true);
    });
  }, []);

  return (
    <div>
      {/* 进度条 */}
      <div style={{ 
        width: '100%', 
        height: '20px', 
        backgroundColor: '#eee',
        marginBottom: '20px'
      }}>
        <div style={{
          width: `${(Object.keys(data).length / totalRequests) * 100}%`,
          height: '100%',
          backgroundColor: done ? 'red' : 'green'
        }} />
      </div>
      {/* 资源表格 */}
      <table border="1">
        <thead>
          <tr>
            <th>ID</th>
            <th>内容</th>
          </tr>
        </thead>
        <tbody>
          {Object.entries(data).map(([id, content]) => (
            <tr key={id}>
              <td>{id}</td>
              <td>{content}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default ResourceTable;

关键改动说明

  • 每个请求不再直接返回fetch的Promise,而是用新Promise包裹整个流程,只有当耗时处理和setState逻辑完成后,才调用resolve()。
  • Promise.all现在等待所有完整请求流程的Promise,确保所有数据更新完成后,再设置done状态隐藏进度条。
  • 这样就能保证Promise.all.then的打印顺序在最后一个Render之后,进度条会在所有数据都渲染完成后才变为红色。
额外优化建议

如果担心频繁调用setState导致性能问题,可以考虑:

  • 批量更新数据:把所有处理后的结果缓存起来,最后一次性setState,但会失去实时进度的显示效果。
  • 使用useReducer替代useState,更高效地处理复杂状态更新逻辑。

内容的提问来源于stack exchange,提问作者0stone0

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 23:22:21