理解closures:构造一个将函数排队在一起的元函数 [英] Understanding closures: Constructing a meta-function that queues functions together

查看:132
本文介绍了理解closures:构造一个将函数排队在一起的元函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在解决问题的方面,我有一个完整的工作解决方案,我刚刚在这里:

In terms of solving the problem, I have a fully working solution that I just finished here:

// 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]();
}

我完全满意这个,因为它做了我想要的,可能比我的第一种方法更容易调试,如果它成功了,本来是。

I am completely happy with this because it does what I want now, and is probably far easier to debug than what my first approach, if it had succeeded, would have been.

但是我无法解决的是为什么我永远不能工作。

But what i can't get over is why I could never get it to work.

看起来像这样。

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();
}

它一直试图调用自己的无限循环,其他奇怪的事情,

It kept trying to call itself in an infinite loop, among other strange things, and it's really hard to identify which function a function is and what a function contains within it, during debugging.

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

I did not dig into the code, but it appears that jQuery.queue has also implemented a similar mechanism to my working one (using an array to track the queue of continuations) rather than using only closures.

我的问题是:可以构建一个Javascript函数,它可以接受一个函数作为参数,并通过其他函数列表增强它,通过构建闭包它创建自己的wrap函数?

My question is this: Is it possible to build a Javascript function that can take a function as argument, and enhance it with a list of other functions, by building closures that wrap functions it creates itself?

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

This is really hard to describe. But I'm sure somebody has a proper theory-backed mathematical term for it.

上述代码引用的是这些例程

P.S. Referenced by the code above are these routines

// 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);
    }
}


推荐答案

问题是你如何为每个新的函数设置 cur_cont 的值,并在中调用 cur_cont c $ c> onload 回调。当你创建一个 tmp_f 时,任何自由变量如 cur_cont 都不是 '到它们的当前值。如果 cur_cont 完全改变,则从 tmp_f 内引用它将引用新的更新值。由于您不断地将 cur_cont 更改为您刚刚创建的新的 tmp_f 函数,对其他函数的引用丢失。然后,当执行 cur_cont 并完成时,再次调用 cur_cont 。这是完全相同的函数,刚刚完成执行 - 因此无限循环!

The problem is how you were setting the value of cur_cont for every new function you made, and calling cur_cont in the onload callback. When you make a closure like tmp_f, any free variables like cur_cont are not 'frozen' to their current values. If cur_cont is changed at all, any reference to it from within tmp_f will refer to the new, updated value. As you are constantly changing cur_cont to be the new tmp_f function you have just made, the reference to the other functions are lost. Then, when cur_cont is executed and finishes, cur_cont is called again. This is exactly the same function that had just finished executing - hence the infinite loop!

在这种情况下,你需要保持自由变量的值内一个闭包,最简单的事情是做一个新的函数,并调用你想要保留的值。通过调用这个新函数,只为该运行创建一个新变量,这将保持你需要的值。

In this sort of situation, where you need to keep the value of a free variable inside a closure, the easiest thing to do is to make a new function and call that with the value you want to keep. By calling this new function, a new variable is created just for that run, which will keep the value you need.


function js_load(resources, cb_done) {
    var cur_cont = cb_done;
    array_each_reverse(resources, function(r) {
        // the stack of callbacks must be assembled in reverse order

        // Make a new function, and pass the current value of the `cur_cont`
        // variable to it, so we have the correct value in later executions.
        // Within this function, use `done` instead of `cur_cont`;
        cur_cont = (function(done) {

            // Make a new function that calls `done` when it is finished, and return it.
            // This function will become the new `cur_cont`.
            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();
                };
            };
        })(cur_cont);

    });

    // Start executing the function chain
    cur_cont();
}

EDIT:实际上,使用 Array.reduce 函数。在概念上,你正在获取一个数组,并从该数组产生一个单一的函数,并且每个连续的函数生成应该依赖于最后生成的函数。这是reduce旨在帮助解决的问题:

Actually, this can be made even simpler by using the Array.reduce function. Conceptually, you are taking an array and producing a single function from that array, and each successive function generated should be dependant upon the last function generated. This is the problem that reduce was designed to help solve:

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();
};

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

Note that reduce and reduceRight are not available for older browsers (<= IE8). A JavaScript implementation can be found on the MDN page.

这篇关于理解closures:构造一个将函数排队在一起的元函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆