在React中處理多併發請求並實現同步處理(如等待所有請求完成後再執行後續操作)是常見需求。以下是一個通用的多併發請求同步處理封裝方案,結合React的useEffectPromise特性實現:

1. 核心封裝函數:處理多請求同步

/**
 * 同步處理多個併發請求
 * @param {Array<() => Promise>} requestList - 請求函數數組(每個函數返回Promise)
 * @param {Function} onSuccess - 所有請求成功後的回調(參數:所有請求結果數組)
 * @param {Function} onError - 任何一個請求失敗後的回調(參數:錯誤信息)
 * @param {boolean} immediate - 是否立即執行(默認true)
 * @returns {() => void} - 手動執行請求的函數
 */
const useConcurrentRequests = (
  requestList,
  onSuccess,
  onError,
  immediate = true
) => {
  // 執行所有請求的函數
  const executeRequests = async () => {
    try {
      // 使用Promise.all併發執行所有請求,等待全部完成
      const results = await Promise.all(requestList.map(req => req()));
      onSuccess?.(results); // 所有請求成功後回調
    } catch (error) {
      onError?.(error); // 任何一個請求失敗則觸發錯誤回調
    }
  };

  // 立即執行(根據immediate控制)
  React.useEffect(() => {
    if (immediate) {
      executeRequests();
    }
  }, [immediate]); // 依賴項:僅immediate變化時重新執行

  return { executeRequests };
};

2. 使用示例:在組件中調用

import React, { useState } from 'react';
import axios from 'axios'; // 假設使用axios發送請求

const MyComponent = () => {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState(null);
  const [error, setError] = useState(null);

  // 1. 定義單個請求函數(返回Promise)
  const request1 = () => axios.get('/api/data1');
  const request2 = () => axios.get('/api/data2');
  const request3 = () => axios.get('/api/data3');

  // 2. 處理所有請求成功的回調
  const handleSuccess = (results) => {
    setLoading(false);
    // results是所有請求的響應數組,按請求順序排列
    setData({
      data1: results[0].data,
      data2: results[1].data,
      data3: results[2].data,
    });
  };

  // 3. 處理請求失敗的回調
  const handleError = (err) => {
    setLoading(false);
    setError(err.message || '請求失敗');
  };

  // 4. 使用封裝的鈎子:傳入請求列表和回調
  const { executeRequests } = useConcurrentRequests(
    [request1, request2, request3], // 請求函數數組
    handleSuccess,                  // 成功回調
    handleError,                    // 失敗回調
    false                           // 不立即執行(手動觸發)
  );

  // 手動觸發請求
  const handleFetch = () => {
    setLoading(true);
    setError(null);
    executeRequests();
  };

  return (
    <div>
      <button onClick={handleFetch} disabled={loading}>
        {loading ? '加載中...' : '獲取數據'}
      </button>
      {error && <div style={{ color: 'red' }}>{error}</div>}
      {data && (
        <div>
          <p>數據1: {JSON.stringify(data.data1)}</p>
          <p>數據2: {JSON.stringify(data.data2)}</p>
          <p>數據3: {JSON.stringify(data.data3)}</p>
        </div>
      )}
    </div>
  );
};

export default MyComponent;

3. 擴展場景:處理部分失敗的情況

如果需要等待所有請求完成(包括失敗的) ,而不是一個失敗就整體失敗,可以使用Promise.allSettled替代Promise.all,修改核心函數如下:

// 改為使用Promise.allSettled(等待所有請求完成,無論成功失敗)
const executeRequests = async () => {
  try {
    const results = await Promise.allSettled(requestList.map(req => req()));
    // 處理結果:區分成功和失敗
    const successResults = [];
    const errorResults = [];
    results.forEach(result => {
      if (result.status === 'fulfilled') {
        successResults.push(result.value);
      } else {
        errorResults.push(result.reason);
      }
    });
    onSuccess?.(successResults, errorResults); // 同時返回成功和失敗結果
  } catch (error) {
    onError?.(error);
  }
};

4. 核心特性説明

  • 併發執行:使用Promise.all(或allSettled)實現請求併發,提高效率
  • 同步處理:等待所有請求完成後再執行後續邏輯(成功/失敗回調)
  • 靈活性:支持立即執行或手動觸發(通過immediate參數控制)
  • 錯誤處理:統一捕獲錯誤,避免單個請求失敗導致整個流程中斷(可按需選擇allallSettled

這種封裝方式適用於需要依賴多個接口數據的場景(如頁面初始化加載多個獨立數據、表單提交前的多接口驗證等),結合React的狀態管理可清晰處理加載、成功、失敗三種狀態。