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

用Promise封装含进度回调的API,适配async/await及进度条关联

处理带进度回调的Promise封装与ProgressBar关联方案

这个问题我之前也遇到过——因为Promise本身是一次性决议(resolve/reject)的,没法直接处理多次触发的进度回调,所以得稍微调整你的Promise适配层,把进度事件的传递通道留出来。下面给你几个实用的方案:

核心思路

我们需要在封装低阶API时,除了返回Promise用于最终的成功/失败处理,还要提供一个订阅进度更新的入口,这样高层的async/await代码就能同时监听进度,关联到你的ProgressBar组件。


方案1:基础JavaScript封装(带进度订阅)

先模拟一下你的低阶API:

// 模拟低阶API:带成功、失败、进度回调
function lowLevelApi(params, successCb, failureCb, progressCb) {
  // 模拟异步操作,每隔500ms更新一次进度
  let currentProgress = 0;
  const timer = setInterval(() => {
    currentProgress += 10;
    progressCb(currentProgress); // 触发进度回调
    if (currentProgress >= 100) {
      clearInterval(timer);
      successCb("操作完成");
    }
  }, 500);
}

然后修改你的Promise适配层,返回一个包含promise和onProgress订阅方法的对象:

function wrapLowLevelApi(params) {
  let progressListeners = [];

  const operationPromise = new Promise((resolve, reject) => {
    lowLevelApi(
      params,
      (result) => resolve(result),
      (error) => reject(error),
      (progressValue) => {
        // 通知所有订阅了进度的回调
        progressListeners.forEach(listener => listener(progressValue));
      }
    );
  });

  return {
    promise: operationPromise,
    // 订阅进度,返回取消订阅的方法(避免内存泄漏)
    onProgress(callback) {
      progressListeners.push(callback);
      return () => {
        progressListeners = progressListeners.filter(l => l !== callback);
      };
    }
  };
}

高层async/await使用(关联ProgressBar)

在你的业务代码里,就可以同时订阅进度并执行异步操作:

async function runOperation() {
  const { promise, onProgress } = wrapLowLevelApi({ /* 你的API参数 */ });

  // 订阅进度,更新ProgressBar组件
  const unsubscribe = onProgress((progress) => {
    // 这里替换成你的ProgressBar更新逻辑,比如React的setState
    console.log(`当前进度:${progress}%`);
    // setProgress(progress); // 示例:React状态更新
  });

  try {
    const result = await promise;
    console.log("操作成功结果:", result);
  } catch (error) {
    console.error("操作失败:", error);
  } finally {
    // 操作结束后取消订阅,避免内存泄漏
    unsubscribe();
  }
}

方案2:TypeScript类型化封装

如果你的项目用TypeScript,可以给封装层加上清晰的类型定义:

// 定义封装后的返回类型
interface WrappedApiResponse<T> {
  promise: Promise<T>;
  onProgress: (callback: (progress: number) => void) => () => void;
}

// 低阶API的类型声明(根据实际情况调整)
type LowLevelApi<T> = (
  params: any,
  successCb: (result: T) => void,
  failureCb: (error: Error) => void,
  progressCb: (progress: number) => void
) => void;

// 类型化的封装函数
function wrapLowLevelApi<T>(params: any, lowLevelApi: LowLevelApi<T>): WrappedApiResponse<T> {
  let progressListeners: ((progress: number) => void)[] = [];

  const promise = new Promise<T>((resolve, reject) => {
    lowLevelApi(
      params,
      (result) => resolve(result),
      (error) => reject(error),
      (progress) => progressListeners.forEach(l => l(progress))
    );
  });

  return {
    promise,
    onProgress(callback) {
      progressListeners.push(callback);
      return () => {
        progressListeners = progressListeners.filter(l => l !== callback);
      };
    }
  };
}

// 使用示例
async function performTypedOperation() {
  const { promise, onProgress } = wrapLowLevelApi<string>({ /* 参数 */ }, lowLevelApi);

  const unsubscribe = onProgress(progress => {
    console.log(`TypeScript进度:${progress}%`);
    // 更新ProgressBar组件状态
  });

  try {
    const result = await promise;
    console.log("Typed操作结果:", result);
  } catch (err) {
    console.error("Typed操作错误:", err);
  } finally {
    unsubscribe();
  }
}

React组件中实际使用示例

如果是在React组件里,结合状态管理来更新ProgressBar:

import { useState } from 'react';

function FileUploadComponent() {
  const [uploadProgress, setUploadProgress] = useState(0);
  const [isUploading, setIsUploading] = useState(false);

  const handleUpload = async () => {
    setIsUploading(true);
    const { promise, onProgress } = wrapLowLevelApi({ /* 上传参数 */ });

    const unsubscribe = onProgress(progress => setUploadProgress(progress));

    try {
      await promise;
      alert("上传成功!");
    } catch (err) {
      alert(`上传失败:${err.message}`);
    } finally {
      unsubscribe();
      setIsUploading(false);
      setUploadProgress(0);
    }
  };

  return (
    <div>
      <button onClick={handleUpload} disabled={isUploading}>
        {isUploading ? "上传中..." : "开始上传"}
      </button>
      <div className="progress-container" style={{ marginTop: '1rem' }}>
        <div 
          className="progress-bar"
          style={{ 
            width: `${uploadProgress}%`,
            height: '1rem',
            backgroundColor: '#4CAF50',
            transition: 'width 0.3s ease'
          }}
        />
        <p>{uploadProgress}%</p>
      </div>
    </div>
  );
}

关键注意点

  • 一定要在操作完成(成功/失败)后取消进度订阅,避免内存泄漏
  • 如果你的进度回调传递的不止数字(比如还有状态描述),只需要调整onProgress的回调参数类型即可
  • 这种方式完全兼容你现有的async/await风格,不需要大幅改动高层代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:22:11