Semáforo, decorador run_once, o algo así

In some places we have to have only one instance of function running.

Este código funciona para mí:

function example() { 
    var that = this; 
    if(that.running) { 
       return false; 
    }
    that.running = true; 
    $.get(url, {}, function (data) {
         that.running = false; 
    }); 
}

How we can improve it, and make it more reusable?

UPD Here is solution, based on Frits van Campen answer:

function make_run_once(callback) {
    callback.running = false;
    return function () {
        if(callback.running) {
            return false;
        }
        callback.running = true;
        deferred = $.Deferred();
        deferred.done(function () {
            callback.running = false;
        });
        callback(deferred);  // pass deferred to callback so it can resolve at it's own leisure
    };
}

preguntado el 28 de agosto de 12 a las 13:08

Is your goal really to have the función only run once, or are you trying to prevent multiple ajax requests from the same object? -

You're basically using this.running. That would not necessarily mean you have one instance of the function running, just 1 instance per this. If you want only 1 instance in total, then use example.running (if example is a top-level function and not an inner-function), or window.running (well, try to avoid naming collisions though). -

@Chris, actually, I have computations, that is going before and after ajax calls, so I want to prevent whole function running. And also preventing possible answer - I'm not interested in async:false :) -

@NikolayFominyh asynch as an option is deprecated, would not have suggested it. However, if you were only trying to prevent multiple concurrent ajax requests, I was going to point you at developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest#abort :) -

@Chris, thanks.. will it work in all browsers? ) -

2 Respuestas

Es posible que desee utilizar jQuery's `Deferred' functionality para eso:

function decorate(fn) {
    var deferred;
    return function(){
        if (!deferred) {
            deferred = new jQuery.Deferred;
            fn(deferred.resolve); // pass in the callback
        }
        return deferred;
    };
}

A decorated function will always return the same Deferred object, which will be resolved with the callback from the original function - started on the first invocation.

Respondido 28 ago 12, 16:08

This function wont 'reset' when you resolve the deferred. I thought that was a requirement. - Martín pescador

Yes, I read "run_once" in the question's title and thought that would be wanted. If you want it to reset, you might add a check for deferred.state()=="resolved" - Bergi

¿Algo como esto?

function make_run_once(callback) {
    var deferred = false;
    return function () {
        if (false === deferred) {
            return;
        } else {
            deferred = $.Deferred();
            deferred.done(function () {
                deferred = false;
            });
            callback(deferred);  // pass deferred to callback so it can resolve at it's own leisure
        }
    };
}

var once = make_run_once(function (deferred) {
    // do stuff, possible ajax stuff
    setTimeout(function () {
        deferred.resolve();  // resolve at some point so it become unblocked again
    }, 100);
});
once(); // run
once(); // block
once(); // block
once(); // block
setTimeout(once, 500);  // run

What about requests that 'replace' other requests, do you have that? Like an auto-complete feature. You'll need to modify it to get that to work. Rather than ignoring new requests, you should keep the newest request and fire it when the last one completes.

Respondido 28 ago 12, 14:08

Looks confusing cause of deferred same name for variable and $.Deferred object. Also deferred object not creating in current example. - Nikolay Fominyh

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.