如何将此异步回调转换为生成器
How can I convert this async callback to a generator?
我很难理解生成器。但我认为我想做的应该是可能的。
我有一个对象Topic
,它可以访问Page
s。最初实现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
是从回调中调用的。
然后,基于我对这篇文章的(糟糕的)理解(从"使用生成器…"开始),我认为这可能会奏效:
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生成器,你有什么?
我确实想重构
Topic
,这样我就可以通过生成器访问它的页面,而不是通过回调。这可行吗,没有太多麻烦?
不,不是真的。你在这里混合了两个概念:
- 用于创建产生结果值的迭代器的生成器,类似于
result
迭代器 - 生成器被滥用来创建一个产生异步操作的迭代器,该迭代器从外部异步驱动,以允许内部的同步控制结构(请参阅此处以获得更深入的解释,或继续阅读您链接的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 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
方法中),但这可能非常令人困惑。它们将保持独立的发电机。
- 将json回调数据转换为日期
- Facebook转换事件回调
- 使用回调hell将构建转换为promise hell
- 如何将此异步回调转换为生成器
- 在nodejs中将回调转换为promise
- 如何重构“;回调金字塔”;转换为基于承诺的版本
- 触发CSS转换,使用来自Javascript的样式,从内部指令完成回调
- Ember.js从回调转换后
- 无法将 NeDB 节点回调转换为 Bacon EventStream
- 在转换结束时调用回调
- Raphaël转换相对于画布的拖动回调坐标
- 如何将具有两个回调的函数调用转换为promise
- 如何通过'这'转换为setTimeout回调
- 如何将回调示例转换为延迟对象
- Node.js:使用Bluebird将模块函数从回调转换为承诺
- 将嵌套回调转换为承诺
- 在.each()回调(D3)中触发平滑转换
- 扩展ES6承诺,将回调转换为Deferred模式
- d3.js:转换回调不会被enter()调用
- 将带有回调的方法转换为返回带有清理的承诺的方法