如何从异步迭代器中产生一系列承诺?

Posted

技术标签:

【中文标题】如何从异步迭代器中产生一系列承诺?【英文标题】:How do I yield a sequence of promises from an async iterator? 【发布时间】:2018-05-13 13:05:51 【问题描述】:

我从一个函数开始,它渲染并产生一系列图像块。

async function* renderAll(): AsyncIterableIterator<Blob> 
  const canvases = await getCanvases();
  for (const canvas of canvases) 
    yield await new Promise<Blob>((resolve, reject) => 
      canvas.toBlob(result =>  if (result) resolve(result); else reject(); );
    );
  

这很好用,但性能并不理想,因为每个承诺都必须在开始下一个操作之前解决。相反,调用者应该决定何时等待承诺,同时仍然保持顺序。

async function* renderAll(): AsyncIterableIterator<Promise<Blob>> 
  const canvases = await getCanvases();
  for (const canvas of canvases) 
    yield new Promise<Blob>((resolve, reject) => 
      canvas.toBlob(result =>  if (result) resolve(result); else reject(); );
    );
  

令我惊讶的是,这无法编译,因为“类型 Blob 不能分配给类型 Promise&lt;Blob&gt;。”进一步检查表明 yield 运算符 unpacks promises 在异步函数中,因此 yield promise 在功能上与 yield await promise 相同。

为什么yield 运算符会这样操作?是否可以从异步迭代器中产生一系列承诺?

【问题讨论】:

【参考方案1】:

出于某种原因,这两个语句似乎具有相同的效果:

yield await promise
yield promise

第一条语句实际上在 javascript 中编译为 yield yield __await(promise),它按预期工作。我认为这个想法是您应该能够返回迭代的元素或元素的承诺,因此await 并不是真正必要的

根据我对async iterator 规范的理解,它应该用于迭代本身是异步的情况下,在您的情况下迭代本身不是异步的,它更像是一种返回交互的异步方法。我会选择:

async function renderAll(): Promise<Iterable<Promise<IBlob>>> 
    const canvases = await getCanvases();
    return (function* () 
        for (const canvas of canvases) 
            yield new Promise<IBlob>((resolve, reject) => 
                canvas.toBlob(result =>  if (result) resolve(result); else reject(); );
            );
        
    )();

async function renderAll4(): Promise<Iterable<Promise<IBlob>>> 
    return (await getCanvases())
        .map(canvas => new Promise<IBlob>((resolve, reject) => 
                canvas.toBlob(result =>  if (result) resolve(result); else reject(); );
            )
        );

【讨论】:

【参考方案2】:

要解决yield 运算符令人惊讶的行为,一种可能是将promise 包装在一个函数中。

async function* renderAll(): AsyncIterableIterator<() => Promise<Blob>> 
  const canvases = await getCanvases();
  for (const canvas of canvases) 
    yield () => new Promise<Blob>((resolve, reject) => 
      canvas.toBlob(result =>  if (result) resolve(result); else reject(); );
    );
  

我不确定这是否是一个好习惯,但它确实允许调用者安排异步工作。

【讨论】:

以上是关于如何从异步迭代器中产生一系列承诺?的主要内容,如果未能解决你的问题,请参考以下文章

事件 vs 流 vs Observables vs 异步迭代器

迭代器生成器以及利用生成器实现单线程的异步并发

用于组合异步迭代器的映射、过滤器和迭代工具

java迭代器

深入Asyncio异步迭代器

异步生成器不是迭代器?