Hi,

This is a common error, you're not alone.

The problem is that the function you're creating in the loop (the
anonymous function assigned to `onComplete`) has an *enduring
reference* to the `qnum` variable, not a *copy* of its value when the
function was created. All copies of your `onComplete` callback use the
same variable, which by the time they use it will have the value as of
the *end* of the loop. This is how closures (functions that "close
over") data work, they have an enduring reference to the variables.
Closures are not complicated[1], there are just a couple of basics
that people tend to misunderstand if they haven't had them explained
to them.

First I'll tell you how it's usually solved, then how you can solve it
with useful features Prototype providers, and then recommend a
completely different approach:

The usual way to solve it is to create a factory function that creates
functions for you that close over a variable they each have a private
copy of, assigning the value of `qnum` at a point in time to those
variables as we create them, like this:

function fillQuiz()
{
    var qnum = 0;
    var tget = 'q' + qnum;

    while ($(tget))
    {
        new Ajax.Updater($(tget), 'qblock.php', {
            method:     'get',
            parameters: {qnum: $(tget).readAttribute('qnum')},
                        // Here we *call* a function that returns a
function
            onComplete: createCallback(qnum)
        });

        qnum++;
        tget = 'q' + qnum;
    }

    function createCallback(q) {
        // This closure closes over `q`, which is private
        // to the closure and never changes.
        return function() {
            qListen(q);
        };
    }
}

function qListen(_me)
{
    window.alert(_me);
}

There, we use `createCallback` to create and return a function for us
that closes over the `q` argument, which is created for each call.
That way, the returned function closes over something that doesn't
change.

The good news is you don't have to write `createCallback`, because
Prototype already has one: `Function#curry`[2]. `Function#curry` does
exactly what we want, it accepts arguments and returns a function that
will call the original function with those arguments. So with
Prototype, that code gets a bit simpler:

function fillQuiz()
{
    var qnum = 0;
    var tget = 'q' + qnum;

    while ($(tget))
    {
        new Ajax.Updater($(tget), 'qblock.php', {
            method:     'get',
            parameters: {qnum: $(tget).readAttribute('qnum')},
            onComplete: qListen.curry(qnum)
        });

        qnum++;
        tget = 'q' + qnum;
    }
}

function qListen(_me)
{
    window.alert(_me);
}

So that's the general solution, and how Prototype helps with it.
What's the completely different approach?

Ajax calls are expensive, and you can only have so many of them going
at a time. Rather than firing off separate ajax requests for *every
question*, I'd strongly recommend firing off one request with a list
of questions, and having the request reply with all of the results in
one go. That eliminates the loop, solving the problem related to the
loop, and reduces server load.

[1] http://blog.niftysnippets.org/2008/02/closures-are-not-complicated.html
[2] http://api.prototypejs.org/language/Function/prototype/curry/

HTH,
--
T.J. Crowder
Independent Software Engineer
tj / crowder software / com
www / crowder software / com

On Jul 15, 5:26 pm, "Heath Jordan" <hjor...@cascadeauto.com> wrote:
> I am using an anonymous function so I c an pass args.  I need the arguments
> to be variable and when they actually get called they are the final state of
> the variable not the state at which I 'think' I am setting them.
>
> function fillQuiz()
>
> {
>
> var qnum = 0;
>
> var tget = 'q' + qnum;
>
> while($(tget)!= null)
>
> {
>
>    new Ajax.Updater($(tget), 'qblock.php', {
>
>      method: 'get',
>
>      parameters: {qnum: $(tget).readAttribute('qnum')},
>
>      onComplete:function(){qListen(qnum)}
>
>    });
>
>    qnum++;
>
>    tget = 'q' + qnum;
>
> }
> }
>
> function qListen(_me)
>
> {
>
>    window.alert(_me);
>
>
>
>
>
>
>
> }

-- 
You received this message because you are subscribed to the Google Groups 
"Prototype & script.aculo.us" group.
To post to this group, send email to prototype-scriptaculous@googlegroups.com.
To unsubscribe from this group, send email to 
prototype-scriptaculous+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/prototype-scriptaculous?hl=en.

Reply via email to