0

I am trying to create a queue of functions to be called to return autocomplete results. Some of them are functions I construct myself from $.getJSON calls and some are provided to me by external developer using what's specified for jQuery UI autocomplete.

For example, here's a fake one provided. I don't know up front if it's truly async or when it might call the callback:

var providedFunction = function(search, response) {
    setTimeout(function() {
        var arr = ['One', 'Two', 'Demo Custom'];
        response($.grep(arr, function (s) { return s.indexOf(search) === 0; }));
    },5000);
};

Then I want to combine it with some number of other $.getJSON calls and not continue until the whole list has finished:

var searchTerm = "Demo";
var allResults = [];
var functionQueue = [];

functionQueue.push(
    $.getJSON( 'http://ws.geonames.org/searchJSON?featureClass=P&style=short&maxRows=50&name_startsWith=' + searchTerm)
    .success( function(data) {
        $.each(data.geonames, function(i,d) { 
            allResults.push(d.name); });
    })
);

functionQueue.push(
    providedFunction(searchTerm, function(data) {
        allResults.push.apply(allResults, data);
    })
);

// wait for all asyc functions to have added their results,
$.when.apply($, functionQueue).done(function() {
    console.log(allResults.length, allResults);
});                

The problem is that $.when does not wait for the provided function to complete. It returns as soon as all $.getJSON calls have completed. So clearly I'm not wiring up the provided function correctly, but I'm not sure how to do it.

Larson S
  • 311
  • 2
  • 7
  • `providedFunction` isn't a deferred object, therefore `$.when` assumes it is resolved immediately. – Kevin B Apr 01 '13 at 19:47
  • @KevinB thanks but I don't know how to convert it to a deferred since I don't control the definition of it (it's passed in to me) – Larson S Apr 01 '13 at 19:55

1 Answers1

2

If you have your heart set on using $.when, you'll want to create an array of deferreds, so you could invoke provided function like this :

functionQueue.push( (function(){
        var df = new $.Deferred();
        providedFunction(searchTerm, function(data) {
            allResults.push.apply(allResults, data);
            df.resolve();
        })
        return df;
    })()
);

Of course, if you feel like being really fancy, you could use this handy utility for casting callback based APIs to promise/deferred based APIs :

function castAPItoDeferred(origFunction){
    return function(){
        var df = new $.Deferred(),
            args = Array.prototype.slice.call(arguments, 0);
        // assume that the API assumes the last arg is the callback
        args.push(function(result){
            df.resolve(result);
        });
        try {
            origFunction.apply(null, args);
        } catch(e) {
           df.reject(e);
        }
        return df;
    }
}

Which would allow you to do something nice like this :

providedFunctionAsDeferred = castAPItoDeferred(providedFunction);

functionQueue.push(
    providedFunctionAsDeferred(searchTerm)
    .success( function(data) {
        allResults.push.apply(allResults, data);
    })
);

Last caveat - if you do go down the second route, remember to hitch/bind your API funcs if they are invoked on objects (like myApi.doSomeAsyncThing)

Finally, an alternative to using $.when would be to keep track of things manually, probably using a counter. e.g., :

var counter = 2; // set your number of ops here

var checkIfFinished = function(){
    if(--counter === 0) {
        triggerAllEventsCompleteFunc(); // replace this with your desired action
    }
}

// call checkIfFinished in all your callbacks
Frances McMullin
  • 5,516
  • 2
  • 18
  • 19
  • Regarding "If you have your heart set on using $.when": is there a better approach to calling an arbitrarily long mixture of .getJSON calls + these other custom functions and not continuing until all have completed? – Larson S Apr 01 '13 at 20:57
  • edited my answer to add that, really it would depend on your specific cases and exactly how much you know in advance / how flexible your code needs to be. – Frances McMullin Apr 01 '13 at 21:02