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

  1. 上传文件.更新用户界面中的进度百分比.
  2. 在用户界面中创建作业(job),返回作业(job)已启动.
  3. 轮询作业(job)并等待其完成.
  4. 返回响应(本例中为转换后的文件).

用户界面基本上将经历以下顺序:

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

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

该功能将使用XMLHttpRequest上载进度功能,如here.然后,它将使用fetch在后端轮询以获取作业(job)状态.最后,当作业(job)返回"完成"时,它将获取并返回转换后的文件.

使用基于promise (非事件emits 器)的方法来实现这一点的正确方式是什么?发电机?

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

async function pollForJob完成(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 percent完成 = Math.ceil((e.loaded / e.total) * 100);
    yield { type: 'update', percent完成 }
  };

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

  xhr.send(fd);
}

这样的事情可能吗(伪代码)?

如果是这样的话,你会如何构建它?如果不是,你会怎么做呢?

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

const iterator = performUploadJob()

for (const data of iterator) {
  switch (data.type) {
    ...
  }
}

推荐答案

是的,this is possible,但是I wouldn't recommend it,因为async iterators are a bad fit是事件emits 器.即使你使用了103<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');
    …
    

    如果最终轮询实际上返回了处理结果,则图片看起来会不同.

  • 对于upload,我建议实现一个微型的"事件循环",其中每个进度事件调用一个提供的事件处理程序.当上载结束并且响应的promise 被解析时(或者当存在错误时,或者如果上载中止时),事件循环终止.

    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(e => resolve(e.target));
    
        xhr.onprogress = e => {
          try {
            onProgress(e);
          } catch(err) {
            fail(err);
          }
        };
    
        xhr.send(fd);
      });
    }
    
    …
    const { status, response } = await upload(file, e => {
      var percentComplete = Math.ceil((e.loaded / e.total) * 100);
      console.log('completion', percentComplete);
    });
    

Javascript相关问答推荐

JavaScript文本区域阻止KeyDown/KeyUp事件本身上的Alt GR +键组合

我应该绑定不影响状态的函数吗?'

使用i18next在React中不重新加载翻译动态数据的问题

如何在angular中从JSON值添加动态路由保护?

如何在JavaScript文件中使用Json文件

JS:XML insertBefore插入元素

将内容大小设置为剩余可用空间,但如果需要,可在div上显示滚动条

为什么我的getAsFile()方法返回空?

创建以键值对为有效负载的Redux Reducer时,基于键的类型检测

如何将zoom 变换应用到我的d3力有向图?

元素字符串长度html

无法读取未定义的属性(正在读取合并)-react RTK

将嵌套数组的元素乘以其深度,输出一个和

如何在尚未创建的鼠标悬停事件上访问和着色div?

ReferenceError:无法在初始化之前访问setData

如何在Java脚本中并行运行for或任意循环的每次迭代

找不到处于状态的联系人

MUI-TABLE:MUI表组件中交替行的不同 colored颜色 不起作用

Google OAuth 2.0库和跨域开放程序的问题-策略错误

观察子组件中的@Output事件emits 器?