Javascript 这个来自YDKJS的委托递归示例到底是如何工作的?

Javascript 这个来自YDKJS的委托递归示例到底是如何工作的?,javascript,promise,generator,Javascript,Promise,Generator,我开始阅读《你不知道JS:Async和性能》一书,但在以下几点上绊倒了:我在心里仔细检查了代码,得到了正确的结果,但无法理解书中对中间步骤的描述 尝试将console.log()插入函数体,尝试调试程序检查调用堆栈,但仍然无法将我的代码心智模型与书中的一致 function run(),它获取生成器函数作为参数,创建其实例并将其运行到底,将每个先前的yielded值传递给next()调用 function run(gen) { var args = [].slice.call( argu

我开始阅读《你不知道JS:Async和性能》一书,但在以下几点上绊倒了:我在心里仔细检查了代码,得到了正确的结果,但无法理解书中对中间步骤的描述

尝试将
console.log()
插入函数体,尝试调试程序检查调用堆栈,但仍然无法将我的代码心智模型与书中的一致

function run()
,它获取生成器函数作为参数,创建其实例并将其运行到底,将每个先前的
yield
ed值传递给
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(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
    值,并获得第一次Ajax调用的承诺
  • 这一承诺是yielded,它又回到了*foo(2) 生成器实例
  • yield*
    将承诺传递回
    *foo(3)
    生成器 例如。另一个
    yield*
    将承诺传递给
    *bar()
    生成器实例。再一次,另一个
    收益率*
    通过了承诺 转到
    运行(…)
    实用程序,它将等待该承诺(对于 第一个Ajax请求)继续
  • 当承诺解决时,其履行消息将被发送到resume
    *bar()
    ,它通过
    yield*
    进入
    *foo(3)
    实例,然后通过
    yield*
    进入
    *foo(2)
    生成器 实例,然后通过
    yield*
    传递到正常的
    yield
    它正在
    *foo(3)
    生成器实例中等待
  • 第一个调用的Ajax响应现在立即从
    *foo(3)
    生成器实例,它将该值作为
    *foo(2
    )实例中的
    yield*
    表达式的结果发送回,以及 分配给其本地
    val
    变量
  • *foo(2)
    内部,使用
    请求(…)
    发出第二个Ajax请求, 其承诺是将返回到*foo(1)实例,然后
    yield*
    一直传播到
    run(..)
    (第7步)。什么时候 承诺解决后,第二个Ajax响应将传播所有 返回到
    *foo(2)
    生成器实例,并分配给 它的局部
    val
    变量
  • 最后,第三个Ajax请求是通过
    请求(..)
    发出的,它的 promise进入运行(…),然后它的分辨率值出现 一路返回,然后返回 等待的
    *bar()
    中产生*
    表达式
  • 在第八步之前,一切都很清楚

    …然后通过
    yield*
    进入正常
    yield
    它正在
    *foo(3)
    生成器实例中等待

    为什么在
    foo(3)
    中等待,而不是在
    foo(2)
    中等待?我认为在履行承诺后,其值(
    1
    )将传递给
    返回收益请求(“http://some.url/?v=“+val)行,代替
    收益率
    ,因此我们在
    foo(1)
    的末尾有
    返回1
    。然后将
    1
    传递给
    val=yield*foo(val-1)行,再次代替
    yield
    ,因此我们在
    foo(2)
    调用中有
    val=1
    。在此之后,进行第二次
    request()
    ,并
    产生
    s对
    foo(3)
    的承诺。 然后
    foo(3)
    yield
    s承诺
    bar()
    ,然后
    bar()
    yield
    s承诺
    run()
    run()
    等待第二个承诺,就像第一个承诺一样,依此类推

    我忽略了什么

    我忽略了什么

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