我开始阅读《您不知道JS:异步和性能》,并在delegating recursion example绊倒了:我在心中仔细检查了代码并获得了正确的结果,但是无法理解其中的中间步骤的描述。书。
试图将console.log()
插入函数主体,尝试使用调试器检查调用堆栈,但仍无法使我的代码思维模型与本书中的代码一致。
function run()
(将生成器函数作为参数),创建其实例并将其运行到最后,将每个先前经过yield
的值传递给next()
调用。
function run(gen) {
var args = [].slice.call( arguments, 1), it;
// initialize the generator in the current context
it = gen.apply( this, args );
// return a promise for the generator completing
return Promise.resolve()
.then( function handleNext(value){
// run to the next yielded value
var next = it.next( value );
return (function handleResult(next){
// generator has completed running?
if (next.done) {
return next.value;
}
// otherwise keep going
else {
return Promise.resolve( next.value )
.then(
// resume the async loop on
// success, sending the resolved
// value back into the generator
handleNext,
// if `value` is a rejected
// promise, propagate error back
// into the generator for its own
// error handling
function handleErr(err) {
return Promise.resolve(
it.throw( err )
)
.then( handleResult );
}
);
}
})(next);
} );
}
示例代码:
function *foo(val) {
if (val > 1) {
// generator recursion
val = yield *foo( val - 1 );
}
return yield request( "http://some.url/?v=" + val );
}
function *bar() {
var r1 = yield *foo( 3 );
console.log( r1 );
}
run( bar );
为了方便起见,我们可以像这样实现function request()
:
function request(url) {
return new Promise(function(resolve){
setTimeout(function(){
resolve( url.match(/v=(\d+)$/)[1] );
},1000);
});
}
这本书提供了以下步骤:
run(bar)
启动*bar()
生成器。foo(3)
为*foo(..)
创建一个迭代器,并将3
作为其val
参数传递。3 > 1
,foo(2)
创建了另一个迭代器,并将2
作为其val
参数传入。2 > 1
,foo(1)
创建了另一个迭代器,并将1
作为其val
参数传入。1 > 1
是false
,因此我们接下来使用request(..)
值调用1
,并为第一个Ajax调用获得承诺。yield
兑现了承诺,回到了*foo(2)
生成器实例。yield *
传递给*foo(3)
生成器
实例。另一个yield *
将承诺传递给*bar()
生成器实例。另一个yield *
又一次通过了诺言
到run(..)
实用程序,该实用程序将等待该诺言(对于
第一个Ajax请求)继续。*bar()
,它通过yield *
进入*foo(3)
实例,然后又通过yield *
到达*foo(2)
生成器
实例,然后通过yield *
到达常规yield
正在*foo(3)
生成器实例中等待。return
*foo(3)
生成器实例,该实例将该值作为yield *
)实例中*foo(2
表达式的结果发送回去,并且
分配给其本地val
变量。*foo(2)
内,使用request(..)
发出第二个Ajax请求,
将其承诺yield
回到*foo(1)
实例,然后
yield *
一直传播到run(..)
(再次执行步骤7)。什么时候
诺言解决,第二个Ajax响应传播所有
返回*foo(2)
生成器实例,并分配给
其本地val
变量。request(..)
发出的
承诺传给run(..)
,然后其解析值到
一路退回,然后return
进去,以便回到
yield *
中正在等待的*bar()
表达式。一切清晰,直到第8步。
...然后通过
yield *
到达常规yield
在*foo(3)
生成器实例中等待。
为什么要在foo(3)
中等待而不在foo(2)
中等待?我认为在Promise实现后,其值(1
)会传递到return yield request( "http://some.url/?v=" + val );
行,而不是yield
,所以我们在{{1}的末尾有return 1
}。然后再次将foo(1)
传递到1
行,代替val = yield *foo( val - 1 );
,因此我们在yield
调用中有val = 1
。之后,再创建第二个foo(2)
,并request()
承诺yield
。
然后foo(3)
foo(3)
承诺yield
,然后bar()
bar()
承诺yield
。 run()
等待第二个Promise,就像第一个Promise一样,依此类推。
我忽略了什么?
答案 0 :(得分:1)
我忽略了什么?
什么都没有。在步骤8和9中,他们应该引用的生成器是foo(1)
而不是foo(3)
创建的生成器。