5

I want function A to finish execution and only after that function B should start executing. When I call function A and then function B, it seems both are executing simultaneously. And after function B completes, I want to call a third function update_dropdown().

My code looks like this:

function A {
    for (var i = 0; i < 5; i++) {
        var promise = $.get(url+i);
        $.when(promise).then(function () {
            $.post(url);
        });
    }
}
function B {
    var x = $.get(url);
    var promise = $.post(url+x);
    $.when(promise0).then(function () {
        update_dropdown();
    });
}

Please can you tell me how I can make these 3 function calls happen sequentially.

Priyanka A
  • 63
  • 1
  • 1
  • 6
  • 1
    Not entirely sure what your goal is, can you explain a little more? – Metalstorm May 04 '14 at 00:14
  • 1
    Assuming: 1. You call function A, want to wait for the $.get to complete, then you want to call function B and finally once B completes you want to call update_dropdown()? – Pepto May 04 '14 at 00:20
  • 1
    If you want an accurate answer, you really have to detail what you want the calling sequence to be. Your code doesn't make a whole lot of sense (and it's pseudo-code) so it isn't very clear what sequence you actually want things to happen in. For example, in `A()`, do you want five `$.get()` operations in parallel and then when they all complete, a `$.post()` operation? In `B()`, do you really want to do a `$.get()` and then without waiting for anything, do a `$.post()`? – jfriend00 May 04 '14 at 02:19
  • 1
    Yes @Pepto your assumption is correct, "You call function A, want to wait for the $.get to complete, then you want to call function B and finally once B completes you want to call update_dropdown()" – Priyanka A May 04 '14 at 06:02
  • In function-A, I want 5 sequential get&post requests. First get then post. This has to happen 5times. Then function-B, has to execute. In B there is a single $get, from which I get some value, and then post that value. (sorry this part is not shown in my code, caused some confusion there.) Then call function update_dropdown(). – Priyanka A May 04 '14 at 06:09
  • Do the 5 initial requests of get, then post have to be serialized (2nd set waits for 1st to complete)? Or can they run all at once and just wait for all of them to be done before proceeding? – jfriend00 May 04 '14 at 06:49
  • And the `url` in the pseudo-code ... presumably it varies? I can't see a reason for calling the same resource many times over in that way. – Roamer-1888 May 04 '14 at 06:55
  • @Roamer-1888, yes the url varies. – Priyanka A May 04 '14 at 20:23
  • @jfriend00 1(get,post),2(get,post),3(get,post),4(get,post),5(get,post). – Priyanka A May 04 '14 at 20:24
  • @PriyankaA - OK, that's the option 1 in my answer if you want them all serialized. It would be faster to execute from start to finish if you could run each pair in parallel, but if #2 pair needs the results of #1 pair or needs the server to have already processed #1 pair, then it would have to be sequential. – jfriend00 May 04 '14 at 22:46

5 Answers5

4

OK, it's getting a little bit clearer what you actually want (based on your recent comments to address clarifying questions) though there are still at least two options open.

For an operation like this, you probably want to take advantage of a number of promise features:

  1. jQuery's Ajax calls already return a promise so you can just use those directly
  2. To serialize operations, you can just chain multiple promise operations together
  3. To make async operations serialize properly, you can return a promise from a .then() handler and the master promise will resolve only when all the chained promises have resolved (kind of a built-in $.when() without having to explicitly call $.when()).
  4. You can chain as many operations together as you want and the master promise will tell you when they are all done.
  5. If you return promises from both A() and B(), then the callers of those functions can monitor when they are done with promise methods which then lets you chain A().then(B) to sequence those two.
  6. When you sequence operations with chaining, the prior methods resolve data is passed to the next .then() handler function in the chain as the first argument to the .then() handler function so if you need the prior data for the next operation, it is right there to use.

So, with all those capabilities, it's just a matter of putting the right scaffolding around the code to implement the exact sequencing you want. Here are two different options:


Option 1: If you want to serialize everything in A() so that all 10 requests happen in serial fashion (the next one proceeds only when the prior one is done), then it could look like this:

// serialize all requests
function A() {
    var p = $.get(url).then(function(data) {return $.post(url)});
    for (var i = 1; i < 5; i++) {
        // chain four more pairs of requests onto the original promise
        p = p.then(function() {return $.get(url)})
             .then(function(data) {return $.post(url)});
    }
    // return the promise so callers can monitor when A() is done
    return p;
}


function B() {
    // sequence these three operations one after the other
    return ($.get(url)
       .then(function(data) {return $.post(url + x)})
       .then(update_dropdown)
    );
}

// run them both, one after the other
A().then(B);

Option 2: If you want the 5 pairs of requests in A() to run in parallel, with only the last part of A() waiting until the 5 pairs of requests are done, then it could look like this:

// parallelize pairs of requests
function A() {
    var promises = [];
    for (var i = 0; i < 5; i++) {
        // execute 5 pairs of requests where each pair is serialized in itself
        promises.push($.get(url).then(function(data) {return $.post(url)}));
    }
    // return a promise that resolves only when all the other promises are done
    return $.when.apply($, promises);
}

function B() {
    // sequence these three operations one after the other
    return ($.get(url)
       .then(function(data) {return $.post(url + x)})
       .then(update_dropdown)
    );
}

// run them both, one after the other
A().then(B);

These use the concept that if you return a promise from a .then() handler function, then it will chain multiple async operations together and the master promise is only resolved when all the chained operations are resolved. This is very powerful for sequencing multiple ajax operations and you can even do it for operations in a loop like you have.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • I tried option2. My requests are not getting generated in the expected sequence. Also requests from function A and B are getting mixed. It is not coming as all requests from function A, followed by all requests from function B. This is what I can see in the network requests log. – Priyanka A May 05 '14 at 19:29
  • @PriyankaA - Option 2 is parallel. From your earlier comments, you want option 1 so things in `A()` are serialized. Also, can you show us your actual code? We can't really debug pseudo-code. If requests from function `A()` and `B()` are getting mixed, then something is not implemented properly because neither option 1 or option 2 will do that. – jfriend00 May 05 '14 at 19:37
  • @PriyankaA - I don't know what you intended to post, but that's about a 100x100 image that is unreadable. If you're posting code, don't do screenshots. Use the edit button and add it to your question. I can't really help you debug without seeing your actual code. – jfriend00 May 05 '14 at 19:38
  • Yes, I completely agree with you. In any case requests from function A and B should not be mixed, may it be option 1 or 2. Here is the link to my [code](http://pastebin.com/Si5T65jh) – Priyanka A May 05 '14 at 20:02
  • @PriyankaA - The first issue I see is that `unattachSTARs()` should end with `return $.when.apply($,promises);`, not `return $.when.apply($.promises);`. Note the comma instead of the period. – jfriend00 May 05 '14 at 20:08
  • @PriyankaA - second issue i see is that `update_AI_starLists()` should end with `}).then(update_dropdown));`, not `}).then(update_dropdown()));`. You do not put `()` on a function you're passing as a reference so it can be called later. – jfriend00 May 05 '14 at 20:10
  • @PriyankaA - excellent! Do you care to mark one of these answers as the best answer (click the green checkmark to the left of the answer)? – jfriend00 May 05 '14 at 20:32
3

Something like this should work

function A {
    var xhr = [];

    for (var i = 0; i < 5; i++) {
        xhr.push( $.get(url) );
    }

    $.when.apply($, xhr).then(B);
}

function B {

    $.get(url).done(function(x) {
        $.post(url + x).done(update_dropdown);
    });

}

Note the use of an array to keep the promises in, then using $.when with apply() to fire a callback when all the ajax requests in the loop has finished.

adeneo
  • 312,895
  • 29
  • 395
  • 388
  • 1
    You don't have the same functionality in functions `A()` and in `B()` as the OP and are missing two operations. – jfriend00 May 04 '14 at 00:39
  • @jfriend00 - In function `A` the `$.when` call is inside the loop in the OP's code, calling the `B` function for each request, I assumed that was the problem and that the point was to move it out of the loop. In the `B` function I'm missing a call to `$.get(url)`, otherwise it's the same, but the OP is calling `$.get(url)` five times in `A`, so I didn't think it very pertinent to call that function again in `B`, and if it was, the procedure to wait for both calls to finish would be the same as in `A` – adeneo May 04 '14 at 01:06
  • Yeah, I guess it isn't entirely clear what calling sequence the OP actually wants. FYI, it probably doesn't matter for `.when()`, but you simulate `$.when(...)` more accurately with `$.when.apply($, xhr);` where you pass `$` instead of `undefined` so `this` is set to `$`. – jfriend00 May 04 '14 at 02:16
  • 1
    Also, `$.when( $.post(url) ).then(update_dropdown);` can just be: `$.post(url).then(update_dropdown);` – jfriend00 May 04 '14 at 02:49
  • 1
    It's probably a good idea to `return` from each function, that way, you will be able to hook on it all being done and compose it. – Benjamin Gruenbaum May 04 '14 at 05:24
1

Assumptions assumptions ...

Let's assume that :

  • the url for every get is the same as that for its corresponding post
  • the urls for each get-post pair should vary
  • the five get-post pairs in A can occur in parallel and we are not interested in the returned data

First, a utility function :

function getThenPost(url, appendToURL) {
    return $.get(url).then(function(x) {
        return (appendToURL) ? $.post(url + x) : $.post(url);
    });
}

then A and B, both of which call the utility :

function A(urls) {
    return $.when.apply(null, urls.map(function(url) {
        return getThenPost(url, false);
    }));
}
function B(url) {
    return getThenPost(url, true);
}

and finally an expression that calls A and B :

A(['/path/0', '/path/1', '/path/2', '/path/3', '/path/4']).then(function() {
    B('/path/5');
}).then(update_dropdown);

It should be reasonably simple to adjust this code if assumptions 1 and 2 are incorrect.

If assumption 3 is incorrect then A will require more extensive modification.

Roamer-1888
  • 19,138
  • 5
  • 33
  • 44
0

We can call our choice function in our way using jquery Deferred Object.

It is very simple let see successfully run example:

<body>
<script
    src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
<script type="text/javascript">

// I want to call function in order of  f1,f2,f3,f4 every time when i will execute this html page.

    promise = f1().then(f2).then(f3).then(f4); // Add handlers to be called when the Deferred object is resolved, rejected, or still in progress.

    function f1() {
        var d = $.Deferred();

        setTimeout(function() {
            // our code here....
            alert("1");
            console.log("1");
            d.resolve(); // resolve() :Resolve a Deferred object and call any doneCallbacks with the given args.
        },1000); // You set some time for each method.
        return d.promise(); //promise(): Return a Deferred’s Promise object.

    }
    function f2() {
        var d = $.Deferred();

        setTimeout(function() {
            alert("2");
            console.log("2");
            d.resolve();
        },1000);
        return d.promise();
    }
    function f4() {
        var d = $.Deferred();

        setTimeout(function() {
            alert("4");
            console.log("4");
            d.resolve();
        },1000);
        return d.promise();
    }
    function f3() {
        var d = $.Deferred();

        setTimeout(function() {
            alert("3");
            console.log("3");
            d.resolve();
        },1000);
        return d.promise();
    }


</script>

Ghanshyam
  • 9
  • 3
-1

Javascript without extra work is single threaded. that means functions are not able to be executed simultaneously. but the problem is that the $.get() and $.post() calls are asynchronous. that means they are executed whenever the requested data arrives your client. (first come first serve)

an solution would be to execute function B after all the results ob A arrived, or to hold back all results and handle all data at once then run update_dropdown().

Bellian
  • 2,009
  • 14
  • 20