5

ES6 有返回迭代器的生成器

function* range(n) {
    for (let i = 0; i < n; ++i) {
        yield i;
    }
}

for (let x of range(10)) {
    console.log(x);
}

有一个关于返回Promises的异步函数的提议:

async function f(x) {
    let y = await g(x);
    return y * y;
}

f(2).then(y => {
    console.log(y);
});

那么如果我将两者结合起来会发生什么,如下所示:

async function* ag(n) {
    for (let i = 0; i < n; ++i) {
         yield i;
    }
}

它返回什么?是Promise<Iterator<Item>>吗?Iterator<Promise<Item>>? 还有什么?我该如何消费它?我想应该有一个相应的for循环,它将异步迭代其结果,例如:

for (await let x of ag(10)) {
    console.log(x);
}

在尝试访问下一个之前等待每个项目可用。

4

2 回答 2

5

Promise<Iterator<Item>>还是Iterator<Promise<Item>>

两者都不。它仍未获得批准,但当前的实现会返回其他内容。Kris Kowal写了一篇关于异步生成器的文章,并参考了Jafar Husain 的 ES7 AsyncGenerator 提案编辑:我们有tc39 提案babel 支持

让我们定义一些类型(简化):

interface Iterator<T> {
  Iteration<T> next();
}

type Iteration<T> = { done: boolean, value: T }

我们正在寻找可以像这样使用的东西:

for (;;) {
    var iteration = await async_iterator.next();
    if (iteration.done) {
        return iteration.value;
    } else {
        console.log(iteration.value);
    }
}

AnIterator<Promise<T>>产生同步迭代,其值为 Promises。它可以这样使用:

for (;;) {
    var iteration = iterator_promise.next();
    if (iteration.done) {
        return await iteration.value;
    } else {
        console.log(await iteration.value);
    }
}

APromise<Iterator<T>>只是一个普通的同步迭代器,从未来开始:

var iterator = await promise_iterator;
for (;;) {
    var iteration = iterator.next();
    if (iteration.done) {
        return iteration.value;
    } else {
        console.log(iteration.value);
    }
}

所以既不合适Iterator<Promise<T>>Promise<Iterator<T>>不合适。目前异步生成器返回AsyncIterators :

interface AsyncIterator<T> {
  Promise<Iteration<T>> next();
}

这完全有道理。移动到迭代器的下一个元素是异步操作,这可以完全按照我们的意愿使用。

如何使用异步生成器?

Babeljs.io 已经编译了异步生成器。Babeljs.io/repl 示例

编辑:自 babel 6 以来,babeljs.io 上没有预设编译异步生成器,通过选项babel-plugin-transform-regenerator支持它。{asyncGenerators:true}

编辑:见transform-async-generator-functionsbabel 6 插件。

function delay(timeout, val) {
  return new Promise(resolve => setTimeout(resolve, timeout, val));
}

async function* asyncGenerator() {
  for (var i = 0; i < 5; i++) {
    await delay(500);
    yield i;
  }
}

async function forAwait(iter, fn) {
  for (;;) {
    let iteration = await iter.next();
    if (iteration.done) return iteration.value;
    await fn(iteration.value);
  }
}


async function main() {
  console.log('Started');
  await forAwait(asyncGenerator(), async item => {
    await delay(100);
    console.log(item);
  });
  console.log('End');
}

main();

有一个用于异步迭代器的便捷for await循环的提议(在Async 迭代中描述):

for await (let line of readLines(filePath)) {
    print(line);
}

更新:

不幸的是,async-await没有成为ECMAScript 2016的一部分。至少await提到了一个保留字以备将来使用。

更新:

相关提案:

于 2016-01-09T14:09:20.000 回答
0

只是想:迭代器函数没有返回值,因此使它们异步是没有意义的。这两种方法之间存在概念上的差距。- 迭代器是基于拉的:您调用迭代器并调用新值的计算 - Promise 是基于推的:Promise 将结果推送给它的侦听器。(一次或从不)

虽然在某些情况下创建一个Iterator<Pomise<Item>>

function* f(g){
    for(...){
        let y = await g();
        yield y;
    }
}

我想不出将 Iterator 包装到 Promise 中的任何情况。因为从定义中实例化迭代器没有任何异步。

于 2016-01-09T13:50:11.527 回答