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

React.js:无法将Promise结果设置为对象数组内的属性

问题描述

API返回的结果里,films、vehicles等属性是URL数组。我想请求这些URL拿到对应的对象数组,再把结果更新回原属性里。用Promise.all处理后,赋值给films和vehicles时,得到的却是Promise状态(有时是pending,有时显示完成但不是对象数组),想要的是让newState里的对象的films和vehicles变成对象数组。

以下是我的代码:

import { useEffect, useState } from "react";
const url = `https://swapi.dev/api/people/`;

export function ToDo() {
  const [results, setResults] = useState([]);

  useEffect(() => {
    apiCallHandler(url);
  }, []);

  async function asyncAllPromises(arr) {
    const promises = await arr.map(async (item) => {
      const response = await fetch(item, { cache: "no-cache" });
      const json = await response.json();
      return json;
    });
    const response = await Promise.all(promises);
    return response;
  }

  const apiCallHandler = async () => {
    try {
      const response = await fetch(url);
      let data = await response.json();

      let newState = data.results.map((result) => {
        result.films = asyncAllPromises(result.films).then((data) => data);
        result.vehicles = asyncAllPromises(result.vehicles).then(
          (data) => data
        );
        return result;
      });

      setResults(newState);
    } catch (error) {
      console.error("Unable to find data", error);
    }
  };

  console.log("results", results);

  const table =
    results && results.length ? (
      <table>
        <thead>
          <tr>
            <td>Name</td>
            <td>Mass</td>
          </tr>
        </thead>
        <tbody>
          {results.map((result, index) => {
            return (
              <tr key={index}>
                <td>{result.name}</td>
                <td>{result.mass}</td>
              </tr>
            );
          })}
        </tbody>
      </table>
    ) : (
      "Loading.."
    );

  return <> {table} </>;
}

解决方案

1. 先修正asyncAllPromises函数的错误

你在arr.map前加了await,但map是同步方法,返回的是Promise数组而非单个Promise,这行await完全多余,会直接返回原数组。修正后:

async function asyncAllPromises(arr) {
  const promises = arr.map(async (item) => {
    const response = await fetch(item, { cache: "no-cache" });
    return response.json();
  });
  return Promise.all(promises);
}

2. 等待所有异步请求完成后再更新状态

原来的map遍历是同步的,内部的异步操作不会等待完成,直接把Promise赋值给了films和vehicles。要改成异步遍历,用Promise.all等待所有结果处理完毕再更新状态:

const apiCallHandler = async () => {
  try {
    const response = await fetch(url);
    const data = await response.json();

    // 对每个结果做异步处理,替换films/vehicles为对象数组
    const newState = await Promise.all(data.results.map(async (result) => {
      // 复制原对象,避免直接修改原数据(可选但推荐)
      const updatedResult = { ...result };
      updatedResult.films = await asyncAllPromises(result.films);
      updatedResult.vehicles = await asyncAllPromises(result.vehicles);
      return updatedResult;
    }));

    setResults(newState);
  } catch (error) {
    console.error("Unable to find data", error);
  }
};

3. 问题根源解释

  • 直接给result.films赋值asyncAllPromises(...),本质是把Promise对象存进了属性,而不是等待Promise resolve后的最终数组
  • map是同步遍历,不会等待内部的异步操作完成,所以newState里的属性全是未完成或刚完成的Promise,不是你要的对象数组

额外优化:完善加载状态

可以加个专门的加载状态,避免初始空数组阶段的异常显示:

const [results, setResults] = useState([]);
const [loading, setLoading] = useState(true);

// 在apiCallHandler里,请求完成后关闭加载状态
const apiCallHandler = async () => {
  try {
    // ... 原有处理代码
    setResults(newState);
  } catch (error) {
    console.error("Unable to find data", error);
  } finally {
    setLoading(false);
  }
};

// 渲染部分调整
const table = loading ? (
  "Loading.."
) : (
  <table>
    {/* 原有表格内容 */}
  </table>
);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 17:00:32