【问题标题】:How can I convert this async callback to a generator?如何将此异步回调转换为生成器?
【发布时间】:2015-05-30 13:42:52
【问题描述】:

我很难理解生成器。但我认为我正在尝试做的应该是可能的。

我有一个对象Topic 可以访问Pages。最初Topic 的实现是为了通过回调来检索Pages。

var Topic = function( id ) {

  var repository = new PageRepository();

  this.id = id;
  this.getAllPages = function( callback ) {
    repository.getAllPagesByTopicId( this.id, function( result ) {
      var pages = [];
      while( result.hasNext() ) {
        pages.push( result.next() );
      }
      callback( pages );
    } );
  }
}

var topic = new Topic( 1 );
topic.getAllPages( function( pages ) {
  console.log( pages ) // received Page instances
} );

现在,假设我无法重构 PageRepository 的回调机制,但我确实想重构 Topic 以便我可以通过生成器访问它的页面,而不是通过打回来。这是否可行,没有太多的麻烦?

我知道我可以使用 for...of 语句迭代生成器值,例如:

var topic = new Topic( 1 );
for( let page of topic.pages() ) {  // create the generator
  console.log( page ); // received next Page
}

...所以我想出了以下内容:

var Topic = function( id ) {

  ...

  this.pages = function*() { // refactored getAllPages () to a generator function pages()
    repository.getAllPagesByTopicId( this.id, function( result ) {
      while( result.hasNext() ) {
        yield result.next(); // yield the next Page
      }
    } );
  }
}

但是,这不起作用,可能是因为在回调中调用了yield

然后,基于我对this article(从“使用生成器......”开始)的(较差的)理解,我认为这可能有效:

var Topic = function( id ) {

  ...

  this.pages = function*() {

    let gen = function*() {}(); // create an inner generator

    // not actually sure why the following wrapper function is needed
    // but something similar is used in mentioned article
    yield function() {
      repository.getAllPagesByTopicId( this.id, function( result ) {
        while( result.hasNext() ) {
          gen.next( result.next() ); // call next() on inner generator
        }
      } );
    }(); // immediately create this mysterious wrapper function
  }
}

不幸的是,这也不起作用。

所以,我想要实现的目标是可行的,没有太多麻烦;含义:没有模块(如 co、suspend 等)和/或复杂的 thunk 生成器,你有什么?

【问题讨论】:

    标签: javascript asynchronous callback generator ecmascript-6


    【解决方案1】:

    我确实想重构 Topic 以便我可以通过生成器而不是通过回调来访问它的页面。这是可行的,没有太多麻烦吗?

    不,不是真的。您在这里混合了两个概念:

    • 用于创建产生结果值的迭代器的生成器,类似于result 迭代器
    • 滥用生成器来创建产生异步操作的迭代器,该操作从外部异步驱动以允许内部同步控制结构(另请参阅 here for deeper explanation,或继续阅读您链接的 davidwalsh 文章)

    一旦第二个获得了自己的关键字 (async/await),merge the two concepts 就有一些想法,但这还不适用(async iteration proposal 处于第 3 阶段)。您被“嵌套”结构困住了,即异步回调中的类似数组的迭代器。

    因此,您可以为其中任何一个使用生成器,但不能同时为两者使用一个生成器。这是否值得值得怀疑。请注意,两者都不会使您的代码同步,在某些方面您总是必须使用异步回调。


    使用生成器实例回调,而不是数组:

    function Topic( id ) {
      var repository = new PageRepository();
      this.id = id;
      this.getAllPages = function( callback ) {
        repository.getAllPagesByTopicId( this.id, function( result ) {
          callback( function* () {
            while( result.hasNext() ) {
              yield result.next();
            }
          }() );
        } );
      }
    }
    
    var topic = new Topic( 1 );
    topic.getAllPages( function( pageiterator ) {
      for( let page of pageiterator ) {
        console.log( page ); // received next Page
      }
    } );
    

    不要回调函数,而是恢复生成器(如 最简单的异步示例):

    function Topic( id ) {
      var repository = new PageRepository();
      this.id = id;
      this.getAllPages = function( it ) {
        repository.getAllPagesByTopicId( this.id, function( result ) {
          for (var pages = [], result.hasNext(); ) pages.push( result.next() );
          it.next( pages );
        } );
      }
    }
    
    var it = (function *() {
      var topic = new Topic( 1 );
      var pages = yield topic.getAllPages( it );
      console.log( pages ) // received Page instances
    }());
    it.next();
    

    是的,您可以同时使用这两种方法(将生成器创建的迭代器传递给异步生成器的 next 方法),但这可能会让人很困惑。而且它们将保持独立的生成器。

    【讨论】:

    • 对不起,我完全忘记了我问了这个问题。谢谢您的回答;我将不得不更深入地研究它,其他时间,因为我还没有完全理解它。那不是因为你;这是因为这个主题只是需要我更多的关注,让我完全理解它。
    • 在您编写“yield topic.getAllPages(it)”的第二种方法Bergi 中,它不会计算getAllPages 方法中的所有内容,然后给您第一个收益吗?我认为@DecentDabbler 想要的是逐页获取,而不是一次获取所有内容。第一种方法似乎像真正的生成器一样逐页返回
    • @PirateApp:是的,但这并不重要,因为一切都可以立即使用。是的,正如我回答的第一部分所解释的那样,你不能真正同时拥有异步和迭代。
    • 我不明白...首先您说它不可行,但随后您继续展示了两种可以完成的方法...
    • @Michael 不,这两个 sn-ps 没有按照 OP 的要求进行操作。它们是仅将生成器用于所要求的方面之一的变通方法。一般来说,这个答案大约有 4 年的历史并且已经过时了。异步迭代器在今天很流行,第二个 sn-p 方法已经被 async/await 弃用了一段时间。
    猜你喜欢
    • 2018-11-24
    • 1970-01-01
    • 2017-10-22
    • 2018-07-09
    • 2019-08-17
    • 2019-11-29
    • 2010-12-20
    • 2015-06-24
    • 1970-01-01
    相关资源
    最近更新 更多