DavidDunham
DavidDunham

Reputation: 1362

Queue function calls

In my fiddle I have following function that is called trice (to simulate the user starting it three times in quick succession)

https://jsfiddle.net/tvs1zdw9/1/

function test(){
  var r = $.Deferred();

  //simulate ajax call that loads data and takes 2 seconds
  setTimeout(function(){
    r.resolve();
    count++;
    $('#here>i').html(count);
  },2000);

  return r;
}

Goal: No matter how quickly the user keeps pressing the button I always want to wait until the function completes until it is run again.

I have already added a deferrer (r) which gives an indication to when the function is done with following code:

test().done(function(){
    console.log('done running function');
});

Example:

I found the jQuery queue functionality but it seems it is only for functions chained to items in the DOM ...perhaps I do not understand it fully. Here: https://api.jquery.com/queue/

Can anyone help me build up a queue and then go through it after each function has completed?

Upvotes: 0

Views: 1492

Answers (2)

Roamer-1888
Roamer-1888

Reputation: 19288

The code can be very simple, even elegant, if you implement :

  • the queue as a promise chain
  • the things to be queued as functions.

First, a reusable function that forms a private queue and returns a function by which items may be added to the queue.

function AsyncQueue(stopOnError) {
    var p = $.when(); // a resolved promise, which acts as the seed of a .then() chain (ie a "queue").
    return function(fn) {
        p = p
          .then(null, function() { if(!stopOnError) return $.when(); }) // prevent propagation of a previous error down the chain.
          .then(fn);
        return p;
    }
}

With this, the queue (the promise chain) will "self-administer" so the need for running and runQueue() disappears.

Now you can write :

function goodDelay() { // simulate a successful request
    return $.Deferred(function(dfrd){ setTimeout(dfrd.resolve, 1000); });
}
function badDelay() { // simulate a failed request
    return $.Deferred(function(dfrd){ setTimeout(dfrd.reject, 1000); });
}

var queue = AsyncQueue(false); // `queue` is a function with its own private .then chain.
queue(goodDelay).then(successHandler, errorHandler);
queue(badDelay).then(successHandler, errorHandler);
queue(goodDelay).then(successHandler, errorHandler);

function successHandler() {
    console.log('success:');
}
function errorHandler() {
    console.log('error:');
}

stopOnError works as follows :

  • false: the internal error handler ensures that the queue always goes down the succcess path regardless of the previous outcome.
  • true: the internal error handler is rendered ineffective and the queue adopts its natural (jQuery) behaviour of propagating the error down the chain. Following an error, further queued functions will not fire, though their external errorHandlers (if present) will fire and will all be informed of the same "reason" for failure.

DEMO with stopOnError false
DEMO with stopOnError true

Upvotes: 1

DavidDunham
DavidDunham

Reputation: 1362

Solution: https://jsfiddle.net/jqqmrv4L/1/

I have created a variable queue

var queue = [];

When the function is called before it is completed I will push to queue.

  var count = 0;
  var running = false;
  var queue = [];
    var test = function(){
    var r = $.Deferred();

    if(!running){
      running = true;

      //simulate ajax call that loads data and takes 2 seconds
      setTimeout(function(){
        r.resolve();
        count++;
        $('#here>i').html(count);
        running = false;
      },2000);
    } else {
        console.log('called whilst running');
      queue.push('234');
    }

    return r;
    }

Then I have another function to handle the queue which is called after the initial function has completed like so:

test().done(function(){
        console.log('done running function, checking queue');
      runqueue();
    });

And here the final "runqueue" function that handles the queue:

function runqueue(){
         if(queue.length){
            console.log('queue has!', queue.length);
        //runqueue();
        test().done(function(){
            console.log('queue item completed');
          queue.splice(0,1); //delete this because it has ran
          //runqueue();
        });
      } else {
        console.log('queue done');
      }
  }

Upvotes: 0

Related Questions