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

如何重构系列API调用以提升执行速度、缩短页面加载耗时

核心性能问题定位

3-5秒加载耗时的根源不是冗余循环,是代码里的几个设计缺陷:

  • 元数据接口完全串行执行:for循环中用await逐个发起请求,总耗时等于所有单个请求耗时的累加。按单个元数据请求平均150ms计算,25个NFT的总请求耗时就达到3.75秒,和你遇到的耗时区间完全吻合。
  • 请求流程存在冗余等待:用字符串状态apiState拆分两个请求步骤,第一次请求完成更新状态后,必须等组件重渲染才能触发第二轮元数据请求,平白增加了空窗期;同时useEffect依赖项缺失apiState、nftData,存在闭包陷阱,可能出现请求不触发、重复触发的异常。
  • 无边界处理逻辑:钱包下无资产时nftData为undefined,直接访问.length会抛错;没有请求失败兜底;无并发控制,资产量大时一次性发几十上百个请求很容易触发接口限流,反而进一步拉长加载时间。
  • 额外性能损耗:请求头、请求配置在组件每次渲染时都会重新创建,可能导致useEffect意外重跑,重复发起请求。
优化方案
  1. 替换串行请求为受控并发请求:通过并发池控制同时发起的请求数量,既把总耗时压缩到批次请求的耗时级别,又避免并发过高触发接口限流。
  2. 移除多余的中间状态,把两个请求流程合并到同一个异步函数中,消除重渲染等待的空窗期。
  3. 把固定不变的请求配置移到组件作用域外,避免重复创建触发不必要的effect重跑。
  4. 增加增量渲染逻辑:每拿到一个NFT的元数据就更新一次视图,不用等所有请求全部返回才展示内容,大幅降低用户感知的加载时长。
  5. 补全空资产、请求异常的兜底逻辑,解决无资产时的报错问题。

优化后可直接运行的代码

// 固定请求配置移到组件外,仅初始化一次,避免重复创建
const myHeaders = new Headers();
myHeaders.append("X-API-Key", CENTER_API_KEY);
const COMMON_REQUEST_OPTIONS = {
  method: 'GET',
  headers: myHeaders,
  redirect: 'follow'
};

/**
 * 并发限流工具
 * @param {number} concurrency 最大并发数
 * @param {Array<() => Promise<any>>} tasks 异步任务列表
 * @returns {Promise<Array<any>>} 所有任务的返回结果
 */
async function asyncPool(concurrency, tasks) {
  const results = [];
  const executingSet = new Set();
  for (const task of tasks) {
    const promise = Promise.resolve().then(task);
    results.push(promise);
    executingSet.add(promise);
    const cleanTask = () => executingSet.delete(promise);
    promise.then(cleanTask, cleanTask);
    if (executingSet.size >= concurrency) {
      await Promise.race(executingSet);
    }
  }
  return Promise.all(results);
}

// NFT网格组件
function NftTileGrid() {
  const [nftList, setNftList] = useState([]);
  const [loading, setLoading] = useState(true);
  const [loadError, setLoadError] = useState(null);

  useEffect(() => {
    const fetchAllNftData = async () => {
      try {
        setLoading(true);
        setLoadError(null);
        setNftList([]);

        // 拉取钱包下的资产列表
        const walletRes = await fetch(walletAPICall, COMMON_REQUEST_OPTIONS);
        const walletData = await walletRes.json();
        const assetList = walletData.items ?? [];

        // 无资产直接结束流程
        if (assetList.length === 0) return;

        // 构造元数据请求任务,保持原列表顺序
        const metaFetchTasks = assetList.map((asset, index) => async () => {
          const { address, tokenId } = asset;
          const metaRes = await fetch(
            `https://api.center.dev/v1/ethereum-mainnet/${address}/${tokenId}`,
            COMMON_REQUEST_OPTIONS
          );
          const metaData = await metaRes.json();
          // 增量渲染:拿到一个元数据就更新一次视图
          setNftList(prev => {
            const next = [...prev];
            next[index] = metaData;
            return next;
          });
          return metaData;
        });

        // 执行并发请求,最大并发数设为6,可根据接口限流规则调整
        await asyncPool(6, metaFetchTasks);
      } catch (err) {
        setLoadError(err);
      } finally {
        setLoading(false);
      }
    };

    fetchAllNftData();
  }, []);

  // 渲染分支
  if (loading) return <div className="loading-tip">资源加载中</div>;
  if (loadError) return <div className="error-tip">加载失败,请刷新重试</div>;
  if (nftList.length === 0) return <div className="empty-tip">当前地址下无关联资产</div>;

  return (
    <div className="nft-tile-grid">
      {nftList.map(nft => {
        // 这里写你的图片磁贴渲染逻辑
        return <img key={`${nft.address}-${nft.tokenId}`} src={nft.image} alt={nft.name} />
      })}
    </div>
  );
}
优化效果

以24个NFT、单个请求平均耗时150ms的场景为例:

  • 原串行逻辑总请求耗时约3.6秒,优化后并发6个请求的场景下,总请求耗时约600ms,速度提升6倍左右。
  • 增量渲染逻辑下,第一个请求返回(约150ms)时用户就能看到第一个图片磁贴,感知加载速度提升更明显。
  • 补全了所有边界场景的处理,不会出现无资产时的报错,也不会因为并发太高被接口限流。
  • 移除了冗余的状态和重复创建的对象,不会出现意外重复请求的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 19:21:13