576

This function below doesn’t work like I want it to; being a JS novice I can’t figure out why.

I need it to wait 5 seconds before checking whether the newState is -1.

Currently, it doesn’t wait, it just checks straight away.

function stateChange(newState) {
  setTimeout('', 5000);

  if(newState == -1) {
    alert('VIDEO HAS STOPPED');
  }
}
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
copyflake
  • 6,237
  • 4
  • 20
  • 14
  • 3
    Possible duplicate of [Execute script after specific delay using JavaScript](https://stackoverflow.com/questions/24849/execute-script-after-specific-delay-using-javascript) – Anonymous Sep 20 '19 at 20:05

14 Answers14

546

Browser

Here's a solution using the new async/await syntax.

Be sure to check browser support as this is a language feature introduced with ECMAScript 6.

Utility function:

const delay = ms => new Promise(res => setTimeout(res, ms));

Usage:

const yourFunction = async () => {
  await delay(5000);
  console.log("Waited 5s");

  await delay(5000);
  console.log("Waited an additional 5s");
};

The advantage of this approach is that it makes your code look and behave like synchronous code.

Node.js

Node.js 16 provides a built-in version of setTimeout that is promise-based so we don't have to create our own utility function:

import { setTimeout } from "timers/promises";

const yourFunction = async () => {
  await setTimeout(5000);
  console.log("Waited 5s");

  await setTimeout(5000);
  console.log("Waited an additional 5s");
};

⚠️ Just for the record, you might be tempted to use a wait function to circumvent race conditions (when testing asynchronous code for example). This is rarely a good idea.

Etienne Martin
  • 10,018
  • 3
  • 35
  • 47
  • 1
    Don't need ECMAScript 6: If you are using promises already to do the async loading and just want to mimic one part of the chain taking a long time, you can add this wait() function to the chain. – Dovev Hefetz Feb 14 '18 at 14:52
  • 7
    This really is the best answer with the new syntax. I had issues with using the wait() solution above. – pianoman102 Mar 22 '19 at 22:39
  • 2
    to be concise: ```const delay = async (ms: number) => new Promise(res => setTimeout(res, ms));``` – Jonathan Nov 01 '20 at 03:07
  • 2
    for the records and future searchs: this can be very helpful for who is actually using Selenium with Javascript and React, because you can wait some seconds while React recalculates the page after e.g. a dropdown selection. – Don Diego Jan 07 '21 at 15:28
  • @RaymondAtivie unluckyly selenium is really hard to be used with react, after some months i gave up and switched to cypress. Not the best for flexibility but well it's good enough and the most important thing: it works – Don Diego Jun 21 '21 at 09:53
490

You have to put your code in the callback function you supply to setTimeout:

function stateChange(newState) {
    setTimeout(function () {
        if (newState == -1) {
            alert('VIDEO HAS STOPPED');
        }
    }, 5000);
}

Any other code will execute immediately.

Joseph Silber
  • 214,931
  • 59
  • 362
  • 292
  • 6
    The main problem is that in some cases (specifically testing) this is woefully inadequate. What if you need to sleep for 500ms before returning from the function, for instance to simulate a _slow_ async http request? – A.Grandt Sep 22 '16 at 09:17
  • 22
    If by "testing" you mean unit tests: your test framework should have a way to run async tests. If you mean manual testing: Chrome's Network tab has a Throttling dropdown to simulate slow requests. – Joseph Silber Sep 23 '16 at 02:02
  • 1
    what if I develop a chrome extension and the last evaluated value in injected script should give me the result; and I need some delays? – mirek May 30 '19 at 17:01
  • 1
    See also the following question: https://stackoverflow.com/questions/758688/sleep-in-javascript-delay-between-actions – GDP2 Aug 26 '20 at 05:17
264

You really shouldn't be doing this, the correct use of timeout is the right tool for the OP's problem and any other occasion where you just want to run something after a period of time. Joseph Silber has demonstrated that well in his answer. However, if in some non-production case you really want to hang the main thread for a period of time, this will do it.

function wait(ms){
   var start = new Date().getTime();
   var end = start;
   while(end < start + ms) {
     end = new Date().getTime();
  }
}

With execution in the form:

console.log('before');
wait(7000);  //7 seconds in milliseconds
console.log('after');

I've arrived here because I was building a simple test case for sequencing a mix of asynchronous operations around long-running blocking operations (i.e. expensive DOM manipulation) and this is my simulated blocking operation. It suits that job fine, so I thought I post it for anyone else who arrives here with a similar use case. Even so, it's creating a Date() object in a while loop, which might very overwhelm the GC if it runs long enough.
But I can't emphasize enough, this is only suitable for testing, for building any actual functionality you should refer to Joseph Silber's answer.

greybeard
  • 2,249
  • 8
  • 30
  • 66
Mic
  • 3,810
  • 1
  • 13
  • 16
  • 8
    This won't stop javascript execute. – Terry Lin Jun 23 '16 at 08:21
  • 14
    So basically you are wasting CPU time. That's not a wait as you are not putting the thread into sleep mode allowing the main processor to focus in other tasks. – Kyordhel Jul 06 '17 at 20:25
  • 10
    @Gzork Thread sleep is only one way to implement a wait function, and it's unfortunately not available in the context of client-side javascript. However, if you're thinking other asynchronous tasks in the client will be completed while it's running, then you obviously haven't tested it. Although I would use it in the main thread, I put together a fiddle illustrating how even if this is called via a setTimeout in the first place, it still interrupts other async events https://jsfiddle.net/souv51v3/1/ - you'll find even the JSFiddle window itself becomes unresponsive while it completes. – Mic Jul 06 '17 at 20:43
  • This worked great for me in the context of unit tests in truffle javascript testing (mocha style) where I was testing a modifier that wouldn't release tokens until a set time in the future - thanks! – Thom Ives Dec 29 '17 at 06:14
  • 1
    Might not be perfect for normal use, but in case `setTimeout` in react-native does not work it is very helpful - for debugging purposes. – Martin Bories Mar 20 '18 at 01:49
  • 2
    Awful solution IMHO -- hogs CPU while it's "sleeping." The right way to sleep is via async/await ala this answer https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep or Etienne's. – David Simic Dec 18 '18 at 21:42
  • 3
    @DavidSimic Unfortunately, promises weren’t implemented in any common browser JavaScript runtime at the time this answer was written. The promised-based solutions won’t block the main UI thread either, which makes them completely unsuited to my use case (at the time) for the same reason setTimeout doesn’t achieve it. If you’ve benchmarked the CPU usage, I’d be interested in the results though. – Mic Dec 18 '18 at 21:54
  • @Mic Fair enough :) -- I was looking at this from nodejs / server side in which case promises would be the way to go at least now - I didn't consider the browser case and the possibility of wanting to block the main UI (the question didn't specify either way so that threw me off). – David Simic Dec 19 '18 at 03:19
  • 1
    @DavidSimic Totally agree. If you just want a sort of thread sleep equivalent, the promise-based solutions are much better/cleaner today. And this was never intended for Node. Even before the runtime brought in native async/await support, there were a few modules around that use C bindings to do a sleep. – Mic Dec 19 '18 at 03:52
  • @Kiquenet yes, so back in 2015 we had fewer native options to isolate work units in the browser, so expensive DOM manipulation and synchronous computations would delay each other. A big calculation could hold up a UI change until it was done, etc. I was working on performance-tuning the ux on an app at the time, and wanted to simulate an expensive DOM manipulation without pulling the big dataset required to do it for real. This wait implementation effectively did the same by keeping the runtime too busy to do other things. – Mic Aug 28 '22 at 12:06
  • 1
    What this does is called 'busy waiting' - I also needed it for some testing in development. For modern JS, you should write `const start` and `let end` instead of `var` in both cases. I also had to rename it to `my_wait` or the page would crash - perhaps some naming conflict. – Cadoiz Jul 24 '23 at 07:31
176

If you're in an async function you can simply do it in one line:

console.log(1);
await new Promise(resolve => setTimeout(resolve, 3000)); // 3 sec
console.log(2);

FYI, if target is NodeJS you can use this built-in function if you want (it's a predefined promisified setTimeout function):

import { setTimeout } from 'timers/promises';

await setTimeout(3000); // 3 sec
Shl
  • 3,130
  • 1
  • 17
  • 16
  • 3
    What is the difference between the two implementations? – EAzevedo May 03 '20 at 08:59
  • For anyone who likes to compare different answers, the only significant difference between this and approved answer is: approved answer presented significant line as an utility function – numan Apr 01 '22 at 05:11
53

Use a delay function like this:

var delay = ( function() {
    var timer = 0;
    return function(callback, ms) {
        clearTimeout (timer);
        timer = setTimeout(callback, ms);
    };
})();

Usage:

delay(function(){
    // do stuff
}, 5000 ); // end delay

Credits: How to delay the .keyup() handler until the user stops typing?

Avatar
  • 14,622
  • 9
  • 119
  • 198
47

You should not just try to pause 5 seconds in javascript. It doesn't work that way. You can schedule a function of code to run 5 seconds from now, but you have to put the code that you want to run later into a function and the rest of your code after that function will continue to run immediately.

For example:

function stateChange(newState) {
    setTimeout(function(){
        if(newState == -1){alert('VIDEO HAS STOPPED');}
    }, 5000);
}

But, if you have code like this:

stateChange(-1);
console.log("Hello");

The console.log() statement will run immediately. It will not wait until after the timeout fires in the stateChange() function. You cannot just pause javascript execution for a predetermined amount of time.

Instead, any code that you want to run delays must be inside the setTimeout() callback function (or called from that function).

If you did try to "pause" by looping, then you'd essentially "hang" the Javascript interpreter for a period of time. Because Javascript runs your code in only a single thread, when you're looping nothing else can run (no other event handlers can get called). So, looping waiting for some variable to change will never work because no other code can run to change that variable.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • 33
    **You cannot just pause javascript execution for a predetermined amount of time**. I think you mean you shouldn't, since you *can* (if you want to hang yourself): `var t = new Date().getTime(); while (new Date().getTime() < t + millisecondsToLockupBrowser);` – Joseph Silber Jan 09 '13 at 01:17
  • 9
    @JosephSilber - OK fine, you could do that, but in practice that doesn't work as many browsers will put up a dialog saying that a script has become unresponsive AND it's a horrible user experience and it's bad for battery life and the page is hung while doing so and... That would be bad. – jfriend00 Jan 09 '13 at 01:43
  • 14
    Well *of course* that would be horrible, and no one should *ever ever ever ever ever ever* do that. I just couldn't resist my inner ["well-actually"](http://tirania.org/blog/archive/2011/Feb-17.html). Sorry. – Joseph Silber Jan 09 '13 at 05:35
18
setTimeout(function() {
     $('.message').hide();
}, 5000);

This will hide the '.message' div after 5 seconds.

Barry Michael Doyle
  • 9,333
  • 30
  • 83
  • 143
hackernewbie
  • 1,606
  • 20
  • 13
13

This solution comes from React Native's documentation for a refresh control:

function wait(timeout) {
    return new Promise(resolve => {
        setTimeout(resolve, timeout);
    });
}

To apply this to the OP's question, you could use this function in coordination with await:

await wait(5000);
if (newState == -1) {
    alert('Done');
}
bearacuda13
  • 1,779
  • 3
  • 24
  • 32
10

Try this:

//the code will execute in 1 3 5 7 9 seconds later
function exec() {
    for(var i=0;i<5;i++) {
        setTimeout(function() {
            console.log(new Date());   //It's you code
        },(i+i+1)*1000);
    }
}
Manoj Sharma
  • 1,467
  • 2
  • 13
  • 20
Steve Jiang
  • 653
  • 7
  • 17
  • If you want to wait for a fixed interval before invoking the callback function, try setInterval() like this [background color toggle code](https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_win_setinterval_clearinterval2) where you don't need the for loop index as with setTimeout(). – Leon Chang Nov 04 '21 at 23:25
6

Based on Joseph Silber's answer, I would do it like that, a bit more generic.

You would have your function (let's create one based on the question):

function videoStopped(newState){
   if (newState == -1) {
       alert('VIDEO HAS STOPPED');
   }
}

And you could have a wait function:

function wait(milliseconds, foo, arg){
    setTimeout(function () {
        foo(arg); // will be executed after the specified time
    }, milliseconds);
}

At the end you would have:

wait(5000, videoStopped, newState);

That's a solution, I would rather not use arguments in the wait function (to have only foo(); instead of foo(arg);) but that's for the example.

Sylhare
  • 5,907
  • 8
  • 64
  • 80
6

You can add delay by making small changes to your function ( async and await ).

const addNSecondsDelay = (n) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, n * 1000);
  });
}

const asyncFunctionCall = async () {

  console.log("stpe-1"); 
  await addNSecondsDelay(5);
  console.log("step-2 after 5 seconds delay"); 

}

asyncFunctionCall();
p.durga shankar
  • 967
  • 8
  • 18
  • This is elegant but is there any way to parameterize the work in resolve? For example to close a jquerui dialog box? – cp. Nov 08 '20 at 04:36
5

Best way to create a function like this for wait in milli seconds, this function will wait for milliseconds provided in the argument:

function waitSeconds(iMilliSeconds) {
    var counter= 0
        , start = new Date().getTime()
        , end = 0;
    while (counter < iMilliSeconds) {
        end = new Date().getTime();
        counter = end - start;
    }
}
  • 2
    This is actually the only wrong answer for this question - you should never make JS code stuck in a loop because it's single threaded. Moreover, you are making the computer to work for no reason in a loop, instead of using an event. – Shl Dec 06 '20 at 09:57
  • Loop blocks single threaded. – Kiquenet Aug 27 '22 at 08:20
  • thats the exact way that no one should ever write code – Dima Nov 28 '22 at 10:10
5

If you have an asyn function you can do:

await new Promise(resolve => setTimeout(resolve, 5000));
sumsumcity
  • 217
  • 4
  • 3
  • Please explain your code so it's more helpful for the community. – ethry Sep 21 '22 at 05:48
  • Inside an async scope (i.e. an async function), you can use the "await" keyword to wait for a Promise to resolve before continuing to the next line of the function. This is functionally equivalent to putting the lines after await into the setTimeout callback and not using async/await at all. When using 'await', make sure to wrap it in a "try/catch" to handle errors. – Kenmore Feb 18 '23 at 16:22
  • This is the cleanest solution in my opinion. – galipmedia Jun 05 '23 at 08:36
0

If you want to wait twice in React.

function WaitComponent() {
  const [isWaitTimeOver, setWaitTimeOver] = useState(false);
  const timerIdRef = useRef <number | null> (null);

  useEffect(() => {
    const wait = (time: number, count: number) => {
      timerIdRef.current = window.setTimeout(function () {
        if (count < 1) {
          setWaitTimeOver(true); // this will rerender the useEffect
        } else {
          clickHandler();
        }
      }, time);
    }

    wait(5000, isWaitTimeOver ? 1 : 0);


    return () => {
      if (timerIdRef.current) {
        clearTimeout(timerIdRef.current);
      }
    }
  }, [isWaitTimeOver])
}
Sanjeet kumar
  • 3,333
  • 3
  • 17
  • 26