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

如何用Promise式(非事件发射器)方案实现前端上传任务流式处理?

基于异步生成器的前端文件上传+任务处理方案

需求说明

需要实现一个前端函数完成以下全流程,且通过迭代器输出状态以更新UI:

  1. 上传文件并实时返回进度百分比
  2. 创建任务后输出「任务已启动」状态
  3. 轮询任务状态直至完成
  4. 返回转换后的文件
    UI需依次展示:上传中(带进度圈)、排队中、处理中、完成状态

用户希望采用**基于Promise的异步生成器(非事件发射器)**实现,目标通过迭代器遍历各阶段状态,以下是修正后的可行方案及原伪代码的问题分析。


原伪代码的核心问题

给出的代码存在几个关键错误:

  • upload是普通生成器,但内部XHR回调函数(onprogress/onload)的yield无法作用于外部生成器——回调属于独立作用域,不能直接使用生成器的yield
  • performUploadJob中yield await upload(getFile())写法错误:普通生成器不能用await,需包装为异步生成器或Promise
  • 缺少轮询所需的wait工具函数实现

正确的异步生成器实现方案

我们将XHR异步事件包装为可迭代的异步生成器,让进度状态能通过yield正确输出,同时串联完整流程:

完整代码实现

// 辅助工具:延迟等待函数
function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// 异步生成器:文件上传+实时进度输出
async function* upload(file) {
  return new Promise((resolve, reject) => {
    const formData = new FormData();
    formData.append('file', file);

    const xhr = new XMLHttpRequest();
    xhr.open('POST', '/upload', true);

    // 实时上传进度回调
    xhr.upload.onprogress = (e) => {
      if (e.lengthComputable) {
        const percentComplete = Math.ceil((e.loaded / e.total) * 100);
        // 输出上传进度状态
        this.yield({ type: 'uploading', percentComplete });
      }
    };

    // 上传完成处理
    xhr.onload = () => {
      if (xhr.status === 200) {
        const response = JSON.parse(xhr.responseText);
        // 输出上传完成状态
        this.yield({ type: 'uploaded', percentComplete: 100 });
        // 返回后端生成的任务ID
        resolve(response.workId);
      } else {
        reject(new Error(`上传失败,状态码:${xhr.status}`));
      }
    };

    // 网络错误处理
    xhr.onerror = () => reject(new Error('上传请求失败,请检查网络'));

    xhr.send(formData);
  });
}

// 异步生成器:任务状态轮询
async function* pollForJobComplete(workId) {
  while (true) {
    try {
      const res = await fetch(`/work/${workId}`);
      const jobStatus = await res.json();

      switch (jobStatus.status) {
        case 'queued':
          yield { type: 'queued' };
          break;
        case 'processing':
          yield { type: 'processing' };
          break;
        case 'complete':
          yield { type: 'result', output: jobStatus.output };
          return; // 任务完成,终止轮询
      }
    } catch (err) {
      yield { type: 'error', message: err.message };
      throw err;
    }
    await wait(2000);
  }
}

// 主异步生成器:串联完整流程
async function* performUploadJob(file) {
  try {
    // 处理上传流程,遍历进度状态
    const uploadGenerator = upload(file);
    let workId;
    for await (const status of uploadGenerator) {
      yield status;
      // 上传完成后获取任务ID
      if (status.type === 'uploaded') {
        workId = await uploadGenerator.next().value;
      }
    }

    // 输出任务启动状态
    yield { type: 'taskStarted', message: '任务已启动' };

    // 复用轮询生成器的状态输出
    yield* pollForJobComplete(workId);
  } catch (err) {
    yield { type: 'error', message: err.message };
  }
}

正确调用方式

由于是异步生成器,必须使用for await...of遍历:

// 假设已通过文件选择器获取file对象
const file = document.querySelector('#fileInput').files[0];

async function handleUploadJob() {
  const jobIterator = performUploadJob(file);
  for await (const status of jobIterator) {
    switch (status.type) {
      case 'uploading':
        // 更新上传进度UI
        updateProgressCircle(status.percentComplete);
        console.log(`上传中:${status.percentComplete}%`);
        break;
      case 'uploaded':
        // 切换到排队状态UI
        showStatus('排队中');
        break;
      case 'taskStarted':
        console.log(status.message);
        break;
      case 'queued':
        showStatus('排队中');
        break;
      case 'processing':
        showStatus('处理中');
        break;
      case 'result':
        showStatus('完成');
        // 处理转换后的文件
        handleOutputFile(status.output);
        break;
      case 'error':
        showError(status.message);
        break;
    }
  }
}

// 启动任务
handleUploadJob();

方案可行性验证

这种基于**异步生成器(Async Generator)**的方案完全可行,优势在于:

  • 用线性的迭代写法处理异步流程,代码逻辑更直观、易维护
  • 所有状态通过统一的yield输出,UI层只需一个循环即可处理所有更新,无需绑定多个事件监听
  • 天然支持错误捕获,可在生成器内部或迭代时统一处理异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 06:00:25