如何将此异步回调转换为生成器?
How can I convert this async callback to a generator?
我很难理解生成器。但我认为我正在尝试做的事情应该是可能的。
我有一个可以访问 Page
的对象 Topic
。最初 Topic
的实现方式是通过回调检索 Page
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() );
}
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(从 "To use a generator ..." 开始)的(糟糕的)理解,我认为这可能有效:
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 等...)and/or 令人费解的 thunk 生成器,你有什么?
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?
不,不是真的。您在这里混合了两个概念:
- 生成器用于创建产生结果值的迭代器,类似于
result
迭代器
- generators abuses to create an iterator yielding asynchronous action,从外部异步驱动以允许内部同步控制结构(另请参阅 here for deeper explanation,或继续阅读您链接的那篇 davidwalsh 文章)
有想法 merge the two concepts once the second has gotten its own keywords (async
/await
), but that's not applicable yet (the async iteration proposal 在第 3 阶段)。您被 "nested" 结构所困,异步回调中的类似数组的迭代器。
因此,您可以为其中任何一个使用生成器,但不能同时为两者使用一个生成器。这是否值得值得怀疑。请注意,两者都不会使您的代码同步,在某些方面您将始终必须使用异步回调。
使用生成器实例而不是数组回调:
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
方法),但这可能会让人感到困惑。他们将保持独立的发电机。
我很难理解生成器。但我认为我正在尝试做的事情应该是可能的。
我有一个可以访问 Page
的对象 Topic
。最初 Topic
的实现方式是通过回调检索 Page
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() );
}
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(从 "To use a generator ..." 开始)的(糟糕的)理解,我认为这可能有效:
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 等...)and/or 令人费解的 thunk 生成器,你有什么?
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?
不,不是真的。您在这里混合了两个概念:
- 生成器用于创建产生结果值的迭代器,类似于
result
迭代器 - generators abuses to create an iterator yielding asynchronous action,从外部异步驱动以允许内部同步控制结构(另请参阅 here for deeper explanation,或继续阅读您链接的那篇 davidwalsh 文章)
有想法 merge the two concepts once the second has gotten its own keywords (async
/await
), but that's not applicable yet (the async iteration proposal 在第 3 阶段)。您被 "nested" 结构所困,异步回调中的类似数组的迭代器。
因此,您可以为其中任何一个使用生成器,但不能同时为两者使用一个生成器。这是否值得值得怀疑。请注意,两者都不会使您的代码同步,在某些方面您将始终必须使用异步回调。
使用生成器实例而不是数组回调:
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
方法),但这可能会让人感到困惑。他们将保持独立的发电机。