10

I read through various threads like this one for example.

But it really escapes me how to accomplish the following:

I have 4 functions, and want them happen one after another in sequence. Notice they are in incorrect order, to get my point across. I want the result that will output "1, 2, 3, 4'

function firstFunction(){
  // some very time consuming asynchronous code...
  console.log('1');
}
function thirdFunction(){
  // definitely dont wanna do this until secondFunction is finished
  console.log('3');
}
function secondFunction(){
  // waits for firstFunction to be completed
  console.log('2');
}
function fourthFunction(){
  // last function, not executed until the other 3 are done.
  console.log('4');
}

I tried to figure out callbacks but am getting lost :(

Isn't there some simple way to do this? Like looping through an array...

Community
  • 1
  • 1
tim
  • 3,459
  • 4
  • 31
  • 36
  • 1
    You may be interested in taking a look at promises and jQuery [deferred objects](http://api.jquery.com/category/deferred-object/). – apsillers Sep 05 '12 at 03:31

5 Answers5

15

It's a great chance to start using jQuery Deferred.

Apart from the callbacks-based solution the code is readable, flexible and highly maintainable

http://jsfiddle.net/zerkms/zJhph/

function firstFunction(){
  var d = $.Deferred();
  // some very time consuming asynchronous code...
  setTimeout(function() {
    console.log('1');
    d.resolve();
  }, 1000);
  return d.promise();
}
function thirdFunction(){
  var d = $.Deferred();
  // definitely dont wanna do this until secondFunction is finished
  setTimeout(function() {
    console.log('3');
    d.resolve();
  }, 1000);
  return d.promise();
}
function secondFunction(){
  var d = $.Deferred();
  setTimeout(function() {
    console.log('2');
    d.resolve();
  }, 1000);
  return d.promise();
}
function fourthFunction(){
  var d = $.Deferred();
  // last function, not executed until the other 3 are done.
  setTimeout(function() {
    console.log('4');
    d.resolve();
  }, 1000);
  return d.promise();
}

firstFunction().pipe(secondFunction).pipe(thirdFunction).pipe(fourthFunction);​

PS: as an example of asynchronous code I've used setTimeout. The main thing is that in the end of the asynchronous part you need to call d.resolve() to continue chaining methods.

Further reading: http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/

zerkms
  • 230,357
  • 57
  • 408
  • 498
  • you should press the outlined checkmark under the up vote/down vote if this is your selected answer. – jeremy Sep 05 '12 at 03:43
  • except it's not really my accepted answer :P cuz I dont understand it! – tim Sep 05 '12 at 03:46
  • @tim: please read PS. Timeouts are for simulating asynchronous. Replace them with your async logic. – zerkms Sep 05 '12 at 03:47
  • "cuz I dont understand it" --- oh, at least it's honest :-S Have you read the "further reading" link? – zerkms Sep 05 '12 at 03:48
  • @zerkms, I'm liking the approach. So, are you saying I prepare each function as a deferred object, and when I'm done I return its .promise() in order to be able to chain these together with .pipe() ? I'd really appreciate if you can explain what you did a bit more. thanks. – tim Sep 05 '12 at 03:52
  • @tim: the next `pipe` is called when previous is resolved by `.resolve()`. That's it. – zerkms Sep 05 '12 at 03:54
  • @zerkms, wait a minute... wouldn't the return statement be exectuted before the timeout is finished? – tim Sep 05 '12 at 03:59
  • @tim: well, I wasn't precise enough: the pipe callback is called only after previous pipe was resolved. So the return statement is executed immediately, and it has a jquery promise object, but what is pipelined will be called only after promise is resolved. – zerkms Sep 05 '12 at 04:08
  • how do you do this if the say, second function has a parameter? `firstFunction().pipe(secondFunction(param)).pipe(thirdFunction).pipe(fourthFunction);`​ ? – Nils Sens Nov 03 '16 at 15:35
  • @NilsSens `pipe(() => secondFunction(param))`. So you pass another function that calls a `secondFunction` with an argument. Because otherwise you immediately call a function. – zerkms Nov 03 '16 at 20:10
  • I found this answer helpful. Just a comment: `$.pipe` is deprecated from jQuery 1.8 in favor of `$.then`. – venkrao Jan 24 '17 at 14:34
1

The idea is you'd do something like the following so that once the first function was done running, it'd know what to run as opposed to you having to figure it out on your own outside the function:

function firstFunction(callback){
  // some very time consuming asynchronous code...
  console.log('1');

  return callback(function(){
    alert("Second function finished.");
    return true;
  });
}
function secondFunction(callback){
  // waits for firstFunction to be completed
  console.log('2');

  return callback();
}

firstFunction(secondFunction);

Also look up .apply() and .call().

jeremy
  • 9,455
  • 4
  • 36
  • 57
  • Shouldn't that be "First function finished" or am I being thick? – John Carter Sep 05 '12 at 03:39
  • It's kind of both, but it mainly is the second function that _actually_ finishes because it's passing the function on to the second function. In other words, that alert will alert when the second function calls the callback, not when the first function calls the second function. – jeremy Sep 05 '12 at 03:40
  • Thats why I cant stand this callback stuff, its really hard to track the goings-on. – tim Sep 05 '12 at 03:54
0

If I'm using callbacks, my working solution now looks like this:

    one(two);
    function one(callb){
        console.log('1');
        callb(three);
    }
    function four(){
        console.log('4');
    }
    function two(callb){
        console.log('2');
        callb(four);
    }
    function three(callb){
        console.log('3');
        callb();
    }

I find that hideous. How am I supposed to keep track of this stuff if there is more than 2-3 sequences? Shudder...

tim
  • 3,459
  • 4
  • 31
  • 36
  • "How am I supposed to keep track of this stuff if there is more than 2-3 sequences?" -- isn't deferreds a panacea? – zerkms Sep 05 '12 at 03:45
  • after reading up on it, it seems to be the "remedy of my ills" (I had to look up panacea...) – tim Sep 05 '12 at 03:55
0

It's been a while and I noticed something about deferreds in jquery documentation, specifically the when core API function.

$.when( $.ajax("test.aspx") ).then(function(ajaxArgs){ 
     alert(ajaxArgs[1]); /* ajaxArgs is [ "success", statusText, jqXHR ] */
});

Code sample taken from http://jqapi.com/#p=jQuery.when

tim
  • 3,459
  • 4
  • 31
  • 36
0

I have played with the Promise, Sequence, Exception, Callback to understand how it works and finally made this code.

Call functions with callback and send result as parameter to another function in sequence and have a catch errors.

function firstFunction(par) {
    return new Promise(function (resolve, reject) {
        console.log("start " + par);
        setTimeout(function (par) {
            console.log(par);
            resolve(par + 1);
        }, 1000, par);
    });
}
function secondFunction(par) {
    return new Promise(function (resolve, reject) {
        console.log("start " + par);
        setTimeout(function (par) {
            console.log(par);
            try{
                throw "Let's make an error...";
            }
            catch(err)
            {
                reject(err);
            }
            resolve(par + 1);
        }, 1000, par);
    })
}
function thirdFunction(par) {
    return new Promise(function (resolve, reject) {
        console.log("start " + par);
        setTimeout(function (par) {
            console.log(par);
            resolve(par + 1);
        }, 1000, par);
    });
}

function CatchError(error) {
    console.log("Exception: " + error);
}

//Break all chain in second function
function ChainBrake() {
    firstFunction(1)
    .then(secondFunction)
    .then(thirdFunction)
    .catch(CatchError);    
}

//Log error and continue executing chain
function ChainContinue() {
    firstFunction(1)
    .catch(CatchError)
    .then(secondFunction)
    .catch(CatchError)
    .then(thirdFunction)
    .catch(CatchError);
}
SV0505
  • 41
  • 3