Synchronous delay in code execution

Viewed 82718

I have a code which needs to be executed after some delay say 5000 ms.Currently I am using setTimeout but it is asynchronous and i want the execution to wait for its return. I have tried using the following:

function pauseComp(ms) 
 {
     var curr = new Date().getTime();
     ms += curr;
     while (curr   < ms) {
         curr = new Date().getTime();
     }
 } 

But the code i want to delay is drawing some objects using raphaeljs and the display is not at all smooth. I am trying to use doTimeout plugin. I need to have a delay only once as the delay and code to be delayed are both in a loop. I have no requirement for a id so I am not using it. For example:

for(i; i<5; i++){ $.doTimeout(5000,function(){
         alert('hi');  return false;}, true);}

This waits for 5 sec befor giving first Hi and then successive loop iterations show alert immediately after the first. What I want it to do is wait 5 sec give alert again wait and then give alert and so on.

Any hints/ suggestions are appreciated!

11 Answers

If you'd like to take advantage of the new async/await syntax, You can convert set timeout to a promise and then await it.

function wait(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log("Done waiting");
      resolve(ms)
    }, ms )
  })
}  

(async function Main() {
  console.log("Starting...")
  await wait(5000);
  console.log("Ended!")
})();

Synchronous wait (only for testing!):

const syncWait = ms => {
    const end = Date.now() + ms
    while (Date.now() < end) continue
}

Usage:

console.log('one')
syncWait(5000)
console.log('two')

Asynchronous wait:

const asyncWait = ms => new Promise(resolve => setTimeout(resolve, ms))

Usage:

(async () => {
    console.log('one')
    await asyncWait(5000)
    console.log('two')
})()

Alternative (asynchronous):

const delayedCall = (array, ms) =>
    array.forEach((func, index) => setTimeout(func, index * ms))

Usage:

delayedCall([
    () => console.log('one'),
    () => console.log('two'),
    () => console.log('three'),
], 5000)

Using the new Atomics API, you can start synchronous delays without performance spikes:

const sleep = milliseconds => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds)

sleep(5000) // Sleep for 5 seconds

console.log("Executed after 5 seconds!")

Here's how you can use the JQuery doTimeout plugin

jQuery('selector').doTimeout( [ id, ] delay, callback [, arg ... ] );

From the docs: "If the callback returns true, the doTimeout loop will execute again, after the delay, creating a polling loop until the callback returns a non-true value."

var start = Date.now();
console.log("start: ", Date.now() - start);
var i = 0;
$.doTimeout('myLoop', 5000, function() {
  console.log(i+1, Date.now() - start);
  ++i;
  return i == 5 ? false : true;
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-dotimeout/1.0/jquery.ba-dotimeout.min.js"></script>

JavaScript is single-threaded

It is impossible to make a synchronous delay in javascript, simply because JavaScript is a single-threaded language. The browser (most common JS runtime environment) has what's called the event loop. So everything that the browser does happens in this very loop. And when you execute a script in the browser, what happens is:

  1. The event loop calls your script
  2. Executes it line by line
  3. Once the script has finished*, the event loop continues running

Notice that all of this is happening during a single frame of the event loop! And that means that no other operation (like rendering, checking for user input, etc.) can happen before the script has exited. (*) The exception is async JavaScript, like setTimeout/Interval() or requestAnimationFrame() which are not run on the main thread. So from event loops prespective, the script has finished running.

This implies that if there were a synchronous delay in JavaScript, the whole browser would have to wait for the delay to finish, and meanwhile it's unable to do anything. So there is no, and there won't be any synchronous delay in JS.

Alternative - Maybe?

The alternative depends on the actual thing you want to do. In my case, I have a requestAnimationFrame() loop. So all I needed to do was to store the time, and check between the old time and new time in the loop.

let timer =
{
   startTime: 0,
   time: 1000,     // time for the counter in milliseconds
   restart: true   // at the beginning, in order to set startTime
};

loop();
function loop()
{
   if(timer.restart === true)
   {
      timer.startTime = Date.now();
      timer.restart = false;
   }
   
   if((Date.now() - timer.startTime) >= timer.time)
   {
      timer.restart = true;
      console.log('Message is shown every second');
      // here put your logic 
   }

   requestAnimationFrame(loop);
}

Solution using function generators. To show that it can be done. Not recommended.

function wait(miliseconds){

  const gen = function * (){
     const end = Date.now() + miliseconds;
     while(Date.now() < end){yield};
     return;
  }
  
  const iter = gen();
  while(iter.next().done === false);
}


console.log("done 0");
wait(1000);
console.log("done 1");
wait(2000);
console.log("done 2");

Node solution

Use fs.existsSync() to delay

const fs = require('fs');
const uuidv4 = require('uuid/v4');

/**
 * Tie up execution for at-least the given number of millis.  This is not efficient.
 * @param millis Min number of millis to wait
 */
function sleepSync(millis) {
    if (millis <= 0) return;
    const proceedAt = Date.now() + millis;
    while (Date.now() < proceedAt) fs.existsSync(uuidv4());
}

fs.existsSync(uuidv4()) is intended to do a few things:

  1. Occupy the thread by generating a uuid and looking for a non-existent file
  2. New uuid each time defeats the file system cache
  3. Looking for a file is likely an optimised operation that should allow other activity to continue (i.e. not pin the CPU)
Related