如何将此异步回调转换为生成器? [英] How can I convert this async callback to a generator?

查看:142
本文介绍了如何将此异步回调转换为生成器?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很难理解发电机。但我想我应该做的是应该可以的。



我有一个对象主题页面 s。最初主题被实现,以便页面将通过回调获取。

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

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

现在,我们假设我不能重构 PageRepository 的回调机制,但是我想要重构主题,以便我可以通过生成器访问它的页面,而不是通过回调。这是可行的,没有太多的匆忙?



我知道我可以迭代一个为...的语句,如:

  var topic = new Topic(1); 
for(let page of topic.pages()){//创建生成器
console.log(page); //收到下一页
}

...所以我想出了一些像以下:

  var Topic = function(id){

...

this.pages = function *(){//将一个生成函数的getAllPages()重构为页面()
repository.getAllPagesByTopicId(this.id,function(result){
while(result.hasNext()){
yield result.next(); //产生下一页
}
});
}
}

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



然后,根据我对(这篇文章(从使用生成器...),我以为这可能会起作用:

  var Topic = function(id){

...

this.pages = function *(){

let gen = function *(){}(); //创建一个内部生成器

//实际上不确定为什么需要以下包装函数
//但是在上述文章中使用了类似的东西
yield function(){
repository.getAllPagesByTopicId(this.id,function(result){
while(result.hasNext()){
gen.next(result.next()); // call next()内部发电机
}
});
}(); //立即创建这个神秘的包装函数
}
}

但这不不幸的是,不幸的是,



所以,我正在努力实现,没有太多的麻烦;意思是:没有模块(如co,挂起等等)和/或卷曲的thunk生成器,你有什么?

解决方案


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


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




  • 用于创建产生结果值的迭代器的生成器,类似于 result iterator

  • 生成器被滥用来创建一个产生异步操作的迭代器,它从外部异步驱动,以允许内部的同步控制结构(另见详细说明,或继续阅读您链接的davidwalsh文章)



有一些想法可以合并两个概念一次第二个已经获得了自己的关键字( async / await ),但这还不适用(异步迭代提案在阶段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主题(1);
topic.getAllPages(function(pageiterator){
for(let page of pageiterator){
console.log(page); //接收下一页
}
});






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

 函数主题(id){
var repository = new PageRepository();
this.id = id;
this.getAllPages = function(it){
repository.getAllPagesByTopicId(this.id,function(result){
for(var pages = [],result.hasNext();) .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 方法的异步生成器),但这可能是很混乱。他们会留下单独的发电机。


I have a hard time understanding generators. But I think what I'm trying to do should be possible.

I have an object Topic that has access to Pages. Originally Topic was implemented such that Pages would be retrieved through a callback.

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

Now, let's assume I cannot refactor PageRepository's callback mechanism, but I do want to refactor Topic such that I can access it's pages through a generator, in stead of through a callback. Is that doable, without too much hastle?

I know I can iterate generator values with a for...of statement, like:

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

... so I came up of with something like the following:

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

However, this doesn't work, probably because yield is called from within the callback.

Then, based my (poor) understandings of this article (from "To use a generator ..." onward), I thought this might work:

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
  }
}

But this doesn't work either, unfortunately.

So, is what I'm trying to achieve doable, without too much hassle; meaning: no modules (like co, suspend, etc...) and/or convoluted thunk generators and what have you?

解决方案

I do want to refactor Topic such that I can access it's pages through a generator, in stead of through a callback. Is that doable, without too much hassle?

No, not really. You are mixing two concepts here:

  • generators used to create iterators yielding result values, similar to the result iterator
  • generators abused to create an iterator yielding asynchronous actions, that is driven asynchronously from the outside to allow synchronous control structures on the inside (see also here for deeper explanation, or continue reading that davidwalsh article you linked)

There are ideas to merge the two concepts once the second has gotten its own keywords (async/await), but that's not applicable yet (the async iteration proposal is in stage 3). You are stuck with a "nested" structure, that array-like iterator inside an asynchronous callback.

So you can use a generator for either, but not one for both. And it's questionable whether that's worth it. Notice that neither will make your code synchronous, you will always have to use async callbacks in some regard.


Call back with a generator instance, not an array:

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


Don't call back a function, but resume a generator (like in the simplest async example):

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

Yes, you can use both approaches at once (passing the generator-created iterator into the next method of the async generator), but that's probably quite confusing. And they'll stay separate generators.

这篇关于如何将此异步回调转换为生成器?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆