来自YDKJS的委派递归示例如何工作?

时间:2019-06-12 08:41:38

标签: javascript promise generator

我开始阅读《您不知道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);
    });
}

这本书提供了以下步骤:

  1. run(bar)启动*bar()生成器。
  2. foo(3)*foo(..)创建一个迭代器,并将3作为其val参数传递。
  3. 因为3 > 1foo(2)创建了另一个迭代器,并将2作为其val参数传入。
  4. 因为2 > 1foo(1)创建了另一个迭代器,并将1作为其val参数传入。
  5. 1 > 1false,因此我们接下来使用request(..)值调用1,并为第一个Ajax调用获得承诺。
  6. yield兑现了承诺,回到了*foo(2) 生成器实例。
  7. 将保证返回的yield *传递给*foo(3)生成器 实例。另一个yield *将承诺传递给*bar() 生成器实例。另一个yield *又一次通过了诺言 到run(..)实用程序,该实用程序将等待该诺言(对于 第一个Ajax请求)继续。
  8. 当承诺解决时,其履行消息将被发送以恢复 *bar(),它通过yield *进入*foo(3)实例,然后又通过yield *到达*foo(2)生成器 实例,然后通过yield *到达常规yield 正在*foo(3)生成器实例中等待。
  9. 现在,第一个呼叫的Ajax响应立即从return *foo(3)生成器实例,该实例将该值作为yield *)实例中*foo(2表达式的结果发送回去,并且 分配给其本地val变量。
  10. *foo(2)内,使用request(..)发出第二个Ajax请求,  将其承诺yield回到*foo(1)实例,然后  yield *一直传播到run(..)(再次执行步骤7)。什么时候  诺言解决,第二个Ajax响应传播所有  返回*foo(2)生成器实例,并分配给  其本地val变量。
  11. 最后,第三个Ajax请求是通过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()承诺yieldrun()等待第二个Promise,就像第一个Promise一样,依此类推。

JSFiddle

我忽略了什么?

1 个答案:

答案 0 :(得分:1)

  

我忽略了什么?

什么都没有。在步骤8和9中,他们应该引用的生成器是foo(1)而不是foo(3)创建的生成器。