Neil Burton
Neil Burton

Reputation: 13

AJAX workflow: How do I order the execution of these functions?

I'm trying to figure the best way to get my functions executing in the correct order.

I have 3 functions

function 1 - squirts OPTIONs into a SELECT via JSON and marks them as selected
function 2 - squirts OPTIONS into a 2nd SELECT and marks them as selected
function 3 - gets the values from the above SELECTs along with some additional INPUT values, does an AJAX GET resulting in JSON data, which is read and populates a table.

With JQuery Onload, I execute:

function1();
function2();
function3();

I'm finding function3 is executing before the SELECTs have been populated with OPTIONS and hence the table has no results, because the values sent in the GET were blank.

I know this is probably a very simple problem and that there are probably a dozen ways to accomplish this, but basically I need the best way to code this so that function3 only runs if function1 and 2 are complete.

I've come into Javascript via the back door having learnt the basics of JQuery first!

Thanks for your assistance.

Upvotes: 0

Views: 1267

Answers (5)

Tauren
Tauren

Reputation: 27243

I recommend you use a Promises library. You can hack simple solutions like other answers suggest, but as your application grows, you'll find you are doing more and more of these hacks. Promises are intended to solve these kinds of problems when dealing with asynchronous calls.

The CommonJS project has several Promises proposals which you should check out. Here is a question I asked on SO about Promises a while back with links to other solutions. Learn more about Promises in this Douglas Crockford video. The whole video is good, but skip to just past half way for promises.

I'm using the FuturesJS library currently as it suits my needs. But there are advantages to other implementations as well. It allows you to do sequences very easily:

// Initialize Application
Futures.sequence(function (next) { 
    // First load the UI description document
    loadUI(next);  // next() is called inside loadUI
})
.then(function(next) {
    // Then load all templates specified in the description
    loadTemplates(next); // next() is called inside loadTemplates
})
.then(function(next) {
    // Then initialize all templates specified in the description
    initTemplates();
});

Even more powerful is when you need to join async events together and do another action when all of the other async events have completed. Here's an example (untested) that will load a bunch of HTML files and then perform an action only once ALL of them have completed loading:

var path = "/templates/",
    templates = ["one.html","two.html","three.html"],
    promises = [];

$.each(templates, function(i,name) {
    promises[i] = Futures.promise();
    var $container = $("<div>");
    $container.load(path+name, function(response,status,xhr) {
        promises[i].fullfill();
    }
});

Futures.join(promises, {timeout: 10000}) // Fail if promises not completed in 10 seconds
    .when(function(p_arr) {
        console.log("All templates loaded");
    })
    .fail(function(p_arr) {
        console.log("Error loading templates");
    });

This might be overkill for your application. But if the application is growing in complexity, using promises will help you in the long run.

I hope this helps!

Upvotes: 1

Hogan
Hogan

Reputation: 70538

fun3() will only run after both are ready. It might run twice. You can fix this with a lock inside fun3() you would need a Singleton to guarantee it works correctly.

var select1ready = false, select2ready = false;

fun1()
{
   // do stuff
   select1ready = true;
   fun3();
}

fun2()
{
  // do stuff
  select2ready = true;
  fun3();
}

fun3()
{
   if (select1ready && select2ready)
   {
   }
}


fun1();
fun2();

Upvotes: 0

user113716
user113716

Reputation: 322562

Javascript executes synchronously, which means that function3 must wait for function2 to complete, which must wait for function1 to complete before executing.

The exception is when you run code that is asynchronous, like a setTimeout, setInterval or an asynchronous AJAX request.

Any subsequent code that relies on the completion of such asynchronous code needs to be called in such a manner that it doesn't execute until the asynchronous code has completed.

In the case of the setTimeout, you could just place the next function call at the end of the function you're passing to the setTimeout.

In the case of an AJAX call, you can place the next function call in a callback that fires upon a completed request.

If you don't want the execution of the subsequent function to occur every time, you can modify your functions to accept a function argument that gets called at the end of the asynchronous code.

Something like:

function function1( fn ) {
    setTimeout(function() {
        // your code 
        // Call the function parameter if it exists
        if( fn ) {
            fn();
        }
    }, 200);
}

function function2() {
    // some code that must wait for function1
}

onload:

// Call function1 and pass function2 as an argument
function1( function2 );

// ...or call function1 without the argument
function1();

// ...or call function2 independently of function1
function2();

Upvotes: 1

hybernaut
hybernaut

Reputation: 636

It's not clear why f1 and f2 are executing before f3.

Also, are you using the preferred $(document).ready() or some variation of onload?

It might be helpful if you provide a reproducible test case.

Upvotes: 0

meder omuraliev
meder omuraliev

Reputation: 186662

invoke function2 inside of function1 and function3 inside of function2.

Upvotes: 0

Related Questions