如何使用基于承诺(非事件发射器)的方法来传输数据?

问题描述 投票:0回答:1

当我在应用程序(前端)中调用函数时,我基本上想这样做:

  1. 上传文件。更新 UI 中的进度百分比。
  2. 创建作业并在 UI 中返回“作业已开始”。
  3. 轮询作业并等待其完成。
  4. 返回响应(在本例中为转换后的文件)。

UI 基本上会经历以下顺序:

  1. 正在上传...(百分比圆圈更新)
  2. 排队...
  3. 处理中...
  4. 完成

所有这些都来自调用一个函数。

该功能将使用

XMLHttpRequest
上传进度功能,如此处。然后,它将使用
fetch
在后端进行轮询以获取作业状态。最后,当作业返回“完成”时,它将获取并返回转换后的文件。

使用基于承诺(非事件发射器)的方法来做到这一点的正确方法是什么?发电机?

async function *performUploadJob() {
  const workId = yield await upload(getFile())
  yield { type: 'processing' }
  const output = await pollForJobComplete(workId)
  yield { type: 'result', output }
}

async function pollForJobComplete(workId) {
  while (true) {
    const res = await fetch(`/work/${workId}`)
    const json = await res.json()
    if (json.status === 'complete') {
      return json.output
    }
    await wait(2000)
  }
}

function *upload(file) {
  var fd = new FormData();
  fd.append("file", file);

  var xhr = new XMLHttpRequest();
  xhr.open("POST", "/upload", true);
  xhr.upload.onprogress = function(e) {
    var percentComplete = Math.ceil((e.loaded / e.total) * 100);
    yield { type: 'update', percentComplete }
  };

  xhr.onload = function() {
    if(this.status == 200) {
      yield { type: 'update', percentComplete: 100 }
    }
  }

  xhr.send(fd);
}

类似的事情可能吗(伪代码)?

如果是这样,您将如何构建它?如果没有,你会做什么?

目标是能够做这样的事情:

const iterator = performUploadJob()

for (const data of iterator) {
  switch (data.type) {
    ...
  }
}
javascript promise generator
1个回答
0
投票

是的,这是可能的,但是我不会推荐它,因为异步迭代器不适合事件发射器。即使您确实使用了

AsyncIterator<ProgressEvent, Response, void>
,使用起来也相当不经济,因为使用
for await … of
循环您不会得到
Response
结果。

  • 对于

    pollJob
    ,异步迭代器就可以了,因为a)你不关心结果(完成后它就会停止)和b)通过轮询,你永远不会比消费者更快。您可以使用类似于您所做的异步生成器来实现此操作:

    async function* pollForJob(workId) {
      while (true) {
        const res = await fetch(`/work/${workId}`)
        if (!res.ok) throw new Error('Failed to poll'); // or ignore and carry on
        yield res.json()
        await wait(2000)
      }
    }
    
    …
    for await (const of pollForJob(upload.jobId)) {
      if (json.status === 'complete') {
        break;
      } else if (json.status === 'running') {
        console.log('job continues for', json.estimatedFinish - Date.now());
      }
    }
    console.log('job has finished');
    …
    

    如果最终的 poll 实际上返回了处理的结果,那么图片看起来会有所不同。

  • 对于

    upload
    ,我建议实现一个微型“事件循环”,其中每个进度事件都调用提供的事件处理程序。当上传结束时,事件循环终止,并且响应的承诺得到解决(或者当出现错误时,或者上传被中止时)。

    function upload(file, onProgress, abortSignal) {
      return new Promise((resolve, reject) => {
        abortSignal?.throwIfAborted();
        var fd = new FormData();
        fd.append("file", file);
    
        var xhr = new XMLHttpRequest();
        xhr.open("POST", "/upload", true);
    
        const stop = done => value => {
          abortSignal?.removeEventListener("abort", fail);
          if (xhr.readyState != 4) xhr.abort();
          reject(err);
        }
        const fail = stop(reject);
        abortSignals?.addEventListener("abort", fail);
    
        xhr.ontimeout = fail;
        xhr.onerror = fail;
        xhr.onload = stop(resolve);
    
        xhr.onprogress = e => {
          try {
            onProgress(e);
          } catch(err) {
            fail(err);
          }
        };
    
        xhr.send(fd);
      });
    }
    
    …
    await upload(file, e => {
      var percentComplete = Math.ceil((e.loaded / e.total) * 100);
      console.log('completion', percentComplete);
    });
    
© www.soinside.com 2019 - 2024. All rights reserved.