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

How do I yield a sequence of promises from an async iterator?

我从一个函数开始,该函数渲染并生成一系列图像 blob。

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(); });
    });
  }
}

令我惊讶的是,这无法编译,因为 "Type Blob is not assignable to type Promise<Blob>." 进一步检查表明 yield 运算符 解包 promises 在异步函数中,这样 yield promise 在功能上等同于 yield await promise.

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

要解决 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(); });
    });
  }
}

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

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

yield await promise
yield promise

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

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

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(); });
            })
        );
}