【发布时间】:2019-06-12 08:41:38
【问题描述】:
我开始阅读 You Don't Know JS: Async and Performance 并在delegating recursion example 绊倒:我在心里仔细阅读了代码并得到了正确的结果,但无法理解书中对中间步骤的描述。
尝试将console.log() 插入函数主体,尝试调试器检查调用堆栈,但仍然无法使我的代码心智模型符合书中的那个。
function run(),它获取生成器函数作为参数,创建它的实例并运行到最后,将每个先前的yielded 值传递给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,因此我们接下来使用1值调用request(..),并为第一次 Ajax 调用返回一个承诺。 - 那个承诺是
yielded out,又回到*foo(2)生成器实例。 -
yield *将该承诺传递回*foo(3)生成器 实例。另一个yield *将承诺传递给*bar()生成器实例。又一个yield *再次通过了承诺 到run(..)实用程序,它将等待该承诺(对于 第一个 Ajax 请求)继续。 - 当 Promise 解决时,它的履行消息被发送到 resume
*bar(),它通过yield *进入*foo(3)实例,然后通过yield *到*foo(2)生成器 实例,然后通过yield *到正常的yield这在*foo(3)生成器实例中等待。 - 第一次调用的 Ajax 响应现在立即从
*foo(3)生成器实例,它将该值作为yield *表达式在*foo(2) 实例中的结果发送回,以及 分配给它的本地val变量。 - 在
*foo(2)内部,使用request(..)发出第二个Ajax 请求, 其承诺是yielded 回到*foo(1)实例,然后yield *一直传播到run(..)(再次执行第 7 步)。什么时候 承诺解决,第二个 Ajax 响应传播所有 回到*foo(2)生成器实例,并分配给 它的本地val变量。 - 最后,第三个Ajax请求是用
request(..)发出的,它的 promise 出去到run(..),然后它的分辨率值来了 一直返回,然后是returned 以便返回*bar()中的等待yield *表达式。
直到第 8 步,一切都很清楚。
...然后通过
yield *到正常的yield这在*foo(3)生成器实例中等待。
为什么在foo(3) 等待,而不是在foo(2) 等待?我想在 Promise 实现之后,它的值(1)被传递到return yield request( "http://some.url/?v=" + val ); 行,而不是yield,所以我们在foo(1) 的末尾有return 1。然后1 被传递到val = yield *foo( val - 1 ); 行,再次代替yield,所以我们在foo(2) 调用中有val = 1。之后,第二个request() 被生成,yields 是对foo(3) 的承诺。
然后foo(3)yields 承诺到bar(),然后bar()yields 承诺到run()。 run() 等待第二个 Promise,就像第一个 Promise 一样,以此类推。
我忽略了什么?
【问题讨论】:
标签: javascript promise generator