0

在解决问题方面,我有一个完全可行的解决方案,我刚刚在这里完成:

// synchronous dynamic script loading. 
// takes an array of js url's to be loaded in that specific order. 
// assembles an array of functions that are referenced more directly rather than 
// using only nested closures. I couldn't get it going with the closures and gave up on it. 

function js_load(resources, cb_done) {
    var cb_list = []; // this is not space optimal but nobody gives a damn 
    array_each(resources, function(r, i) {
        cb_list[i] = function() {
            var x = document.body.appendChild(document.createElement('script'));
            x.src = r;
            console.log("loading "+r);
            x.onload = function() { 
                console.log("js_load: loaded "+r); 
                if (i === resources.length-1) {
                    cb_done();
                } else {
                    cb_list[i+1]();
                }
            }; 
        };
    });
    cb_list[0]();
}

我对此非常满意,因为它可以满足我现在的需求,并且可能比我的第一种方法(如果它成功的话)更容易调试。

但我无法克服的是为什么我永远无法让它工作。

它看起来像这样。

function js_load(resources, cb_done) {
    var cur_cont = cb_done;
    // So this is an iterative approach that makes a nested "function stack" where 
    // the inner functions are hidden inside the closures. 
    array_each_reverse(resources, function(r) {
        // the stack of callbacks must be assembled in reverse order
        var tmp_f = function() {
            var x = document.body.appendChild(document.createElement('script'));
            x.src = r;
            console.log("loading "+r);
            x.onload = function() { console.log("js_load: loaded "+r); cur_cont(); }; // TODO: get rid of this function creation once we know it works right 
        };
        cur_cont = tmp_f; // Trying here to not make the function recursive. We're generating a closure with it inside. Doesn't seem to have worked :(
    });
    cur_cont();
}

它一直试图在无限循环中调用自己,以及其他奇怪的事情,并且在调试过程中很难识别函数是哪个函数以及函数包含什么。

我没有深入研究代码,但似乎jQuery.queue也实现了与我的工作类似的机制(使用数组来跟踪延续队列),而不是仅使用闭包。

我的问题是:是否有可能构建一个可以将函数作为参数的 Javascript 函数,并通过构建包含它自己创建的函数的闭包来使用其他函数列表来增强它?

这真的很难描述。但我敢肯定,有人有一个适当的理论支持的数学术语。

PS 上面代码引用的就是这些例程

// iterates through array (which as you know is a hash), via a for loop over integers
// f receives args (value, index)
function array_each(arr, f) {
    var l = arr.length; // will die if you modify the array in the loop function. BEWARE
    for (var i=0; i<l; ++i) {
        f(arr[i], i);
    }
}

function array_each_reverse(arr, f) {
    var l = arr.length; // will die if you modify the array in the loop function. BEWARE
    for (var i=l-1; i>=0; --i) {
        f(arr[i], i);
    }
}
4

1 回答 1

1

问题是您如何cur_cont为创建的每个新函数设置值,并cur_contonload回调中调用。当您像 一样进行闭包时tmp_f,任何像这样的自由变量cur_cont不会“冻结”到它们的当前值。如果cur_cont完全更改,则从内部对其进行的任何引用都tmp_f将引用新的更新值。由于您不断地更改cur_cont为刚刚创建的新tmp_f功能,因此对其他功能的引用将丢失。然后,whencur_cont被执行并完成,cur_cont再次被调用。这与刚刚完成执行的函数完全相同——因此是无限循环!

在这种情况下,您需要将自由变量的值保留在闭包中,最简单的方法是创建一个新函数并使用您想要保留的值调用它。通过调用这个新函数,将为该运行创建一个新变量,它将保留您需要的值。

功能 js_load(资源,cb_done){
    var cur_cont = cb_done;
    array_each_reverse(资源,函数(r){
        // 回调堆栈必须以相反的顺序组装

        // 创建一个新函数,并传递 `cur_cont` 的当前值
        // 变量,所以我们在以后的执行中有正确的值。
        // 在这个函数中,使用 `done` 而不是 `cur_cont`;
        cur_cont = (函数(完成) {

            // 创建一个新函数,在完成时调用 `done`,并返回它。
            // 这个函数将成为新的 `cur_cont`。
            返回函数(){

                var x = document.body.appendChild(document.createElement('script'));
                x.src = r;
                console.log("加载中"+r);
                x.onload = 函数() {
                    console.log("js_load: 加载"+r);
                    完毕();
                };
            };
        })(cur_cont);

    });

    // 开始执行函数链
    cur_cont();
}

编辑:实际上,这可以通过使用该Array.reduce功能变得更简单。从概念上讲,您正在获取一个数组并从该数组中生成一个函数,并且生成的每个连续函数都应依赖于生成的最后一个函数。这是 reduce 旨在帮助解决的问题:

function js_load(resources, done) {
    var queue = resources.reduceRight(function(done, r) {
        return function() {
            var x = document.body.appendChild(document.createElement('script'));
            x.src = r;
            console.log("loading "+r);
            x.onload = function() {
                console.log("js_load: loaded "+r);
                done();
            };
        };
    }, done);

    queue();
};

请注意reducereduceRight不适用于旧版浏览器 (<= IE8)。可以在MDN 页面上找到 JavaScript 实现。

于 2013-03-24T23:48:45.327 回答