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

Node.js中如何通过Promise.all实现循环多API调用的统一结果返回

问题修复:批量API调用的Promise异步控制

你的代码核心问题是没正确利用Promise.all实现批量异步控制,且错误处理逻辑不符合Promise的失败触发规则,无法实现「全成功才返回、一个失败就报错」的需求。以下是具体修正方案:

关键问题点

  1. getChunkUploadResponse$错误处理逻辑错误:你用resolve返回错误信息,但Promise.all仅在Promise被reject时才判定操作失败,错误场景必须用reject抛出异常。
  2. 未收集Promise到数组:声明了promiseAll但未将getChunkUploadResponse$的返回值存入,导致Promise.all完全未生效。
  3. 过早触发resolve:getChunkBatchDetailsResponse$在request回调末尾直接resolve(batchMessage),未等待所有分片上传完成就返回结果。
  4. 回调式request未Promise化:内部request调用为回调风格,未用Promise包裹,导致异步流程失控。

修正后的完整代码

1. 修复分片上传的Promise函数

function getChunkUploadResponse$(id, directory, buffer, userToken) {
  return new Promise((resolve, reject) => { // 新增reject参数用于抛出错误
    request.post(
      `${configJSON.apigeeBaseUrl}/UploadChunk`,
      {
        formData: {
          file: {
            value: buffer,
            options: {
              filename: `${directory.name}.zip`,
            },
          },
        },
        headers: {        
          Authorization: `Bearer ${userToken}`,
        },
      },
      (err, response) => {
        logMessage('err performing batch chunk upload:', JSON.stringify(err));
        logMessage('response performing batch chunk upload:', JSON.stringify(response));

        if (err) {
          // 错误场景用reject抛出,触发Promise.all的失败逻辑
          reject({
            id,
            directory: directory.path,
            message: err.message,
            code: 500,
          });
          return;
        }

        const statusCode = response.statusCode;
        const batchMessage = { // 用局部变量替代this,避免上下文混乱
          id,
          directory: directory.path,
          message: response.statusMessage,
          code: statusCode,
        };

        if (statusCode === 201) {
          try {
            batchMessage.batch = JSON.parse(response.body);
            logMessage('Successfully performed a batch chunk upload');
            resolve(batchMessage);
          } catch (parseErr) {
            // JSON解析失败也视为错误抛出
            reject({
              id,
              directory: directory.path,
              message: `Failed to parse response: ${parseErr.message}`,
              code: 500,
            });
          }
        } else if (statusCode === 413) {
          try {
            reject({
              id,
              directory: directory.path,
              message: JSON.parse(response.body),
              code: 413,
            });
          } catch (parseErr) {
            reject({
              id,
              directory: directory.path,
              message: 'Payload too large',
              code: 413,
            });
          }
        } else {
          let message = response.statusMessage;
          try {
            message = JSON.parse(response.body);
          } catch {}
          batchMessage.message = message;
          // 非201状态码均视为失败,用reject抛出
          reject(batchMessage);
        }
      },
    );
  });
}

2. 修复批量处理的主函数

function getChunkBatchDetailsResponse$(id, baseUri, formData, totalFileSize, chunkSize, userToken) {
  return new Promise((resolve, reject) => {
    const options = {
      method: 'POST',
      url: `${configJSON.apigeeBaseUrl}/batch`,
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${userToken}`,
      },
      body: {} // 补充你的请求body内容
    };

    request(options, (error, response) => {
      if (error) {
        reject({ id, message: error.message, code: 500 });
        return;
      }
      if (!response.body) {
        reject({ id, message: 'Empty response from batch API', code: 400 });
        return;
      }

      // 分片切割逻辑优化
      const chunksArray = [];
      let chunkStart = 0;
      let chunkEnd = chunkSize;
      const totalNumberOfChunks = Math.ceil(totalFileSize / chunkSize);
      
      for (let i = 0; i < totalNumberOfChunks; i++) {
        chunkEnd = Math.min(chunkStart + chunkSize, totalFileSize);
        chunksArray.push(formData.buffer.subarray(chunkStart, chunkEnd));
        chunkStart = chunkEnd;
      }

      // 收集所有分片上传的Promise
      const promiseAll = chunksArray.map((chunkBuffer, index) => {
        const chunkNumber = index + 1;
        logMessage(`Starting upload for chunk ${chunkNumber} of ${totalNumberOfChunks}`);
        // 传入必要参数(directory需根据实际业务场景补充)
        return getChunkUploadResponse$(id, directory, chunkBuffer, userToken);
      });

      // 用Promise.all等待所有分片上传完成
      Promise.all(promiseAll)
        .then((allResults) => {
          // 所有上传成功,返回汇总结果
          resolve({
            id,
            message: 'All chunks uploaded successfully',
            results: allResults
          });
        })
        .catch((error) => {
          // 任意分片失败,直接返回错误
          reject(error);
        });
    });
  });
}

核心规则说明

  • Promise.all特性:仅当所有传入的Promise都resolve时,才会返回所有结果的数组;只要有一个Promisereject,就会立即抛出该错误。
  • 错误处理必须用reject:不要用resolve返回错误对象,否则Promise.all会将其视为成功结果。
  • 异步流程需串行等待:必须等所有异步操作完成后,再调用resolve或reject,禁止提前触发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 12:59:54