Proper way to wait for one function to finish before continuing?
Asked Answered
C

12

364

I have two JS functions. One calls the other. Within the calling function, I'd like to call the other, wait for that function to finish, then continue on. So, for example/pseudo code:

function firstFunction(){
    for(i=0;i<x;i++){
        // do something
    }
};

function secondFunction(){
    firstFunction()
    // now wait for firstFunction to finish...
    // do something else
};

I came up with this solution, but don't know if this is a smart way to go about it.

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()
    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

Is that legit? Is there a more elegant way to handle it? Perhaps with jQuery?

Congressional answered 3/2, 2014 at 1:11 Comment(7)
What does firstFunction exactly do that makes it asynchronous? Either way - check about promisesBust
The first function is rapidly updating a score clock every 10th of a second. Which...come to think of it, I suppose we could pre-calculate and then just manually pause the second function call via setTimeout. That said, I can still see a desire to have a pause ability elsewhere.Congressional
you don't need a pause - google for promises in jqueryBust
@Bust promises looks interesting! Still investigating browser support...Congressional
I also have the same problem.Albers
I think your solution is still valid today for simple scenario like mine.Dowser
I also need that kind of thing; for an app using file system, I want to call in file replacing function several times in another function and wait for the write process to finnish before calling the function another time.Microscopy
C
243

One way to deal with asynchronous work like this is to use a callback function, eg:

function firstFunction(_callback){
    // do some asynchronous work
    // and when the asynchronous stuff is complete
    _callback();    
}

function secondFunction(){
    // call first function and pass in a callback function which
    // first function runs when it has completed
    firstFunction(function() {
        console.log('huzzah, I\'m done!');
    });    
}

As per @Janaka Pushpakumara's suggestion, you can now use arrow functions to achieve the same thing. For example:

firstFunction(() => console.log('huzzah, I\'m done!'))


Update: I answered this quite some time ago, and really want to update it. While callbacks are absolutely fine, in my experience they tend to result in code that is more difficult to read and maintain. There are situations where I still use them though, such as to pass in progress events and the like as parameters. This update is just to emphasise alternatives.

Also the original question doesn't specificallty mention async, so in case anyone is confused, if your function is synchronous, it will block when called. For example:

doSomething()
// the function below will wait until doSomething completes if it is synchronous
doSomethingElse()

If though as implied the function is asynchronous, the way I tend to deal with all my asynchronous work today is with async/await. For example:

const secondFunction = async () => {
  const result = await firstFunction()
  // do something else here after firstFunction completes
}

IMO, async/await makes your code much more readable than using promises directly (most of the time). If you need to handle catching errors then use it with try/catch. Read about it more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function .

Circumstance answered 3/2, 2014 at 1:24 Comment(8)
callbacks are inconvenient to deal with asynchrony, promises are much easier and flexibleBust
While you are right that I shouldn't have used the word best (updated), the convenience of callbacks vs promises depends on the complexity of the problem.Circumstance
It becomes offtopic, but I personally don't see a reason to prefer callbacks these days :-) Cannot remember any of my code written in last 2 years provided callbacks over promises.Bust
If you want, you can use arrow function in es6 secondFunction(){ firstFunction((response) => { console.log(response); }); }Adalie
@JanakaPushpakumara Updated my old answer.Circumstance
if there is any 'big job' inside firstFunction, lets say, an ajax request, it will, probably, fire _callBack() before finish .... I mean, firstFunction will also fire any content in it without wait the previous comand be finishesElanaeland
what if you have no access to the first function?Derbyshire
What is the difference between your last function and this one async function secondFunction() { const result = await firstFunction() // do something else here after firstFunction completes }, and where the await chain stops when you need the value of the functions to continue since you still will need to await secondFunction(...) that will need and async function to be called?Neisse
S
116

Use async/await :

async function firstFunction(){
  for(i=0;i<x;i++){
    // do something
  }
  return;
};

then use await in your other function to wait for it to return:

async function secondFunction(){
  await firstFunction();
  // now wait for firstFunction to finish...
  // do something else
};
Sequacious answered 17/8, 2018 at 11:38 Comment(15)
for those of us stuck supporting older browsers, IE doesn't support async/awaitEcclesiology
Can this be used outside both functions, as in, $(function() { await firstFunction(); secondFunction(); }); ?Rocha
@Rocha Just remember await can only be used inside an async method. So if you make your parent function async you can call as many async methods inside with or without await that you want to.Sequacious
Is it possible to await a setTimeout?Blasius
@Blasius yes it is, wrap setTimeout in another function which resolves a promise after timeout.Sequacious
What is firstFunction() is doing multiple uploads... how will secondFunction() know when it has completed all its jobs?Stila
@volumeone See the return statement in first functionSequacious
Should async function firstFunction() instead be function firstFunction()?Whenas
@Whenas Are you saying that because there is no await inside there ? this is only pseudo code so in actual implementation there will be an await or some blocking operation for which we want to wait.Sequacious
What happens if firstFunction has parameters? How to call it with await?Coopt
@Coopt That should work like a function normally passes parameters.Sequacious
What is IE? in 2022Swallow
@Ecclesiology I'm just circling back to say that IE is no longer with us :D So essentially my approach was future proof ;)Sequacious
In this specific example (I have a very similar code), what is the use of async on firstFunction ?Fine
And now how you await for second function to finish then use its data? where the chain of await will finish? Also you cannot await inside $(function() { ...}); since you cannot make that function async.Neisse
T
86

An elegant way to wait for one function to complete first is to use Promises with async/await function.


  1. Firstly, create a Promise. The function I created will be completed after 2s. I used setTimeout in order to demonstrate the situation where the instructions would take some time to execute.
  2. For the second function, you can use async/await a function where you will await for the first function to complete before proceeding with the instructions.

Example:

    //1. Create a new function that returns a promise
    function firstFunction() {
      return new Promise((resolve, reject) => {
          let y = 0
          setTimeout(() => {
            for (i=0; i<10; i++) {
               y++
            }
             console.log('Loop completed.')  
             resolve(y)
          }, 2000)
      })
    }
    
    //2. Create an async function
    async function secondFunction() {
        console.log('Before promise call.')
        //3. Await for the first function to complete
        const result = await firstFunction()
        console.log('Promise resolved: ' + result)
        console.log('Next step.')
    }; 

    secondFunction()

Note:

You could simply resolve the Promise without any value like so resolve(). In my example, I resolved the Promise with the value of y that I can then use in the second function.

Tradescantia answered 16/4, 2019 at 0:20 Comment(1)
I need to run secondFunction after pageLoad (an event trigger) which is not async function. and I need to have data from secondFunction to continue. how does promise solves this?Neisse
H
59

Notice

This answer is outdated by today's standards. Consider using native JavaScript Promises and async/await syntax instead.


It appears you're missing an important point here: JavaScript is a single-threaded execution environment. Let's look again at your code, note I've added alert("Here"):

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()

    alert("Here");

    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

You don't have to wait for isPaused. When you see the "Here" alert, isPaused will be false already, and firstFunction will have returned. That's because you cannot "yield" from inside the for loop (// do something), the loop may not be interrupted and will have to fully complete first (more details: Javascript thread-handling and race-conditions).

That said, you still can make the code flow inside firstFunction to be asynchronous and use either callback or promise to notify the caller. You'd have to give up upon for loop and simulate it with if instead (JSFiddle):

function firstFunction()
{
    var deferred = $.Deferred();
    
    var i = 0;
    var nextStep = function() {
        if (i<10) {
            // Do something
            printOutput("Step: " + i);
            i++;
            setTimeout(nextStep, 500); 
        }
        else {
            deferred.resolve(i);
        }
    }
    nextStep();
    return deferred.promise();
}

function secondFunction()
{
    var promise = firstFunction();
    promise.then(function(result) { 
        printOutput("Result: " + result);
    });
}

On a side note, JavaScript 1.7 has introduced yield keyword as a part of generators. That will allow to "punch" asynchronous holes in otherwise synchronous JavaScript code flow (more details and an example). However, the browser support for generators is currently limited to Firefox and Chrome, AFAIK.

Hyksos answered 3/2, 2014 at 9:59 Comment(4)
You saved me. $.Deferred() is what I am have been gunning for. ThanksBrigandage
@Hyksos I tried this. But the waitForIt method does not get called at all. What am I doing wrong?Huntlee
@vigamage, could you provide a link to jsfiddle or codepen of what you've tried?Hyksos
@LodeAlaert this answer is a bit outdated by today's standards. You should be using native JavaScript Promises and async/await syntax. Lots of good tutorials on the web, e.g., try www.freecodecamp.org.Hyksos
P
31

I wonder why no one have mentioned this simple pattern? :

(function(next) {
  //do something
  next()
}(function() {
  //do some more
}))

Using timeouts just for blindly waiting is bad practice; and involving promises just adds more complexity to the code. In OP's case:

(function(next) {
  for(i=0;i<x;i++){
    // do something
    if (i==x-1) next()
  }
}(function() {
  // now wait for firstFunction to finish...
  // do something else
}))

a small demo -> http://jsfiddle.net/5jdeb93r/

Pedagogics answered 11/10, 2020 at 16:26 Comment(5)
This worked for me and yes. Good point, I wonder why no one mentioned this. Not all the time one wants to use sync/asyncMccafferty
I'm unfamiliar with JQuery (which I think this uses, based on the settings in the jsfiddle demo) and am quite perplexed with how this works. Can someone explain why the functions don't have a name, and why this entire code snippet is in parentheses? (function(next)...) Also, how does next work when it is never declared? Is it a native JQuery function?Whenas
@velkoon, it has nothing to do with jQuery but is pure javascript as it is intended. The struct or pattern is called a self-invoking function. The next param is actually declared, if you look carefully, the second function() is what is injected as next by the function itself. Anywhere you need something to be executed after something else, you can copy paste the first 4 lines and simply place the code you want executed between the two {...} literals.Pedagogics
@Pedagogics what if the first function fails, of if I dont want to wait for the first function not more then 2 seconds?Donau
Hey @JaberAlNahian, that is actually a completely different scenario than the asked question. In the solution above, since both functions is within the same scope, I would set an let executed = false-flag, and wrap the first function into a setTimeout(); if executed not is true when timeout is met, execute next(), and set executed to true .. (not tested) And, oh - change to if (i==x-1 && !executed) next() to prevent execution twice ...Pedagogics
S
8

The only issue with promises is that IE doesn't support them. Edge does, but there's plenty of IE 10 and 11 out there: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise (compatibility at the bottom)

So, JavaScript is single-threaded. If you're not making an asynchronous call, it will behave predictably. The main JavaScript thread will execute one function completely before executing the next one, in the order they appear in the code. Guaranteeing order for synchronous functions is trivial - each function will execute completely in the order it was called.

Think of the synchronous function as an atomic unit of work. The main JavaScript thread will execute it fully, in the order the statements appear in the code.

But, throw in the asynchronous call, as in the following situation:

showLoadingDiv(); // function 1

makeAjaxCall(); // function 2 - contains async ajax call

hideLoadingDiv(); // function 3

This doesn't do what you want. It instantaneously executes function 1, function 2, and function 3. Loading div flashes and it's gone, while the ajax call is not nearly complete, even though makeAjaxCall() has returned. THE COMPLICATION is that makeAjaxCall() has broken its work up into chunks which are advanced little by little by each spin of the main JavaScript thread - it's behaving asychronously. But that same main thread, during one spin/run, executed the synchronous portions quickly and predictably.

So, the way I handled it: Like I said the function is the atomic unit of work. I combined the code of function 1 and 2 - I put the code of function 1 in function 2, before the asynch call. I got rid of function 1. Everything up to and including the asynchronous call executes predictably, in order.

THEN, when the asynchronous call completes, after several spins of the main JavaScript thread, have it call function 3. This guarantees the order. For example, with ajax, the onreadystatechange event handler is called multiple times. When it reports it's completed, then call the final function you want.

I agree it's messier. I like having code be symmetric, I like having functions do one thing (or close to it), and I don't like having the ajax call in any way be responsible for the display (creating a dependency on the caller). BUT, with an asynchronous call embedded in a synchronous function, compromises have to be made in order to guarantee order of execution. And I have to code for IE 10 so no promises.

Summary: For synchronous calls, guaranteeing order is trivial. Each function executes fully in the order it was called. For a function with an asynchronous call, the only way to guarantee order is to monitor when the async call completes, and call the third function when that state is detected.

For a discussion of JavaScript threads, see: https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23 and https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

Also, another similar, highly rated question on this subject: How should I call 3 functions in order to execute them one after the other?

Siamese answered 7/7, 2018 at 4:20 Comment(2)
To the downvoter(s), I'd be curious to know what's wrong with this answer.Siamese
This is an old reply, and I haven't voted on it, but it could be made clearer if you provide insight into the before/after of functions 1, 2, 3 - instead of only describing it with words. Also, using callbacks could make sense - that way, you can pass function1 and function3 to function2, so that one is executed at the start, and the other when the async operation returns.Coolie
W
6

Can use Promise

A promise is a JavaScript construct that represents a future unknown value. It could be the result from an API call, or it could be an error object from a failed network request. You're guaranteed to get something.

const promise = new Promise((resolve, reject) => {
    // Make a network request
   if (yourcondition=="value") {
      
   } else {
      reject(error);
   }
})

promise.then(res => {
    //if not rejected, code

}).catch(err => {
    //return false; 
})

A promise can have

fulfilled - action successfully completed

rejected - action failed

pending - neither action has been completed

settled - has been fulfilled or rejected

Whicker answered 26/3, 2021 at 9:15 Comment(2)
This adds little to Jakub's answer, and is much less helpful.Coolie
But you aren't guaranteed to get the value when you ask for it.Israel
B
2

Your main function will call firstFunction then on complete of it your next function will execute.

async firstFunction() {
            const promise = new Promise((resolve, reject) => {
                for (let i = 0; i < 5; i++) {
                    // do something
                    console.log(i);
                    if (i == 4) {
                        resolve(i);
                    }
                }
            });
            const result = await promise;
        }
    
        second() {
            this.firstFunction().then( res => {
                // third function call do something
                console.log('Gajender here');
            });
        }
Benares answered 14/5, 2020 at 10:58 Comment(0)
D
1

I wrote this after two days of trying. This is the best way try like this:

var recv = -1;

async function First_fn() {

    var answ = await Second_fn();
    console.log(answ);
    recv = -1;

}

async function Second_fn() {
    var parm = 1;
    var adrss = 2;

    await Third_fn(adrss, parm);
    // Using the following loop will minimize the waiting time
    for (let i = 0; i < 180; i++) {
        if (recv !== -1) {
            console.log(recv);
            break;
        }
        else {
            await new Promise(resolve => setTimeout(resolve, 500));
        }
    }
    return recv;
}

async function Third_fn(adrss, parm) {
    //someting to do => result
    //for example:
    let result = adrss + parm;
    recv = result; // recv is global varable
 }
 
First_fn(); 
Davao answered 1/6, 2022 at 8:8 Comment(1)
Your answer could be improved by adding more information on what the code does and how it helps the OP.Deoxyribonuclease
K
0

This what I came up with, since I need to run several operations in a chain.

<button onclick="tprom('Hello Niclas')">test promise</button>

<script>
    function tprom(mess) {
        console.clear();

        var promise = new Promise(function (resolve, reject) {
            setTimeout(function () {
                resolve(mess);
            }, 2000);
        });

        var promise2 = new Promise(async function (resolve, reject) {
            await promise;
            setTimeout(function () {
                resolve(mess + ' ' + mess);
            }, 2000);
        });

        var promise3 = new Promise(async function (resolve, reject) {
            await promise2;
            setTimeout(function () {
                resolve(mess + ' ' + mess+ ' ' + mess);
            }, 2000);
        });

        promise.then(function (data) {
            console.log(data);
        });

        promise2.then(function (data) {
            console.log(data);
        });

        promise3.then(function (data) {
            console.log(data);
        });
    }

</script>
Klausenburg answered 24/10, 2019 at 2:52 Comment(2)
Please add at least jus a high level comments for description of the given snippet. This is to explain how your code addresses the problem.Tying
The problem was to run a second function after the first one. I show how run a second function after the first one, and also how to run a third function after the second one. I made three functions, to make it easy to understand what code is required.Klausenburg
W
-1

Try this

function firstFunction(){
    
    // do something  
    X=true;
    return X;
}

function secondFunction(){
    X=false;
    X=firstFunction();
    setTimeout( function() {
        if(X==true){
            // do something else
        }else{
            // do something else
        }
        alert(X); // To check X 
       
    }, 100); // Can increase time delay 200,300, ...
}

Increase the time from 100 to 200, 300, ... Based on the time required for the firstFunction to complete

Whicker answered 28/8, 2021 at 4:58 Comment(1)
How is this better than OP's code?Coolie
M
-1

I had the same Issue.
My Solution was to call my second function at the end of my async function,
since you already have a For loop in your async function,
you could call your second function once the highest value of the loop condition has been reached
which in your case is
i = x-1 so:

function firstFunction(){
  for(i=0;i<x;i++){
     // do something
     if(i === x - 1){
       // do something else or call secondFunction();
     }
  }
}
Moffat answered 15/3, 2022 at 10:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.