【问题标题】:How exactly does this delegating recursion example from YDKJS work?这个来自 YDKJS 的委托递归示例究竟是如何工作的?
【发布时间】: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);
    });
}

本书提供了以下步骤:

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

JSFiddle

我忽略了什么?

【问题讨论】:

    标签: javascript promise generator


    【解决方案1】:

    我忽略了什么?

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

    【讨论】:

      猜你喜欢
      • 2021-01-27
      • 2018-03-03
      • 2013-03-09
      • 2013-07-06
      • 1970-01-01
      • 1970-01-01
      • 2019-01-05
      • 2018-01-19
      • 2021-07-05
      相关资源
      最近更新 更多