javascript sleep function

I’ve been looking for the javascript sleep function for a long time. Unfortunately, although most programming languages such as C/C++, php, python, etc. have a sleep function, javascript  has neither a sleep function nor a delay function that can pause the execution for some time and continue to execute the following code. Javascript designers are proud of this. They say if you ever have the need for a sleep function, you are on the wrong way. You should re-design your code to eliminate the need for such a function. You should split your code into several functions. This situation is analogous to Einstein’s theory of relativity to Newton’s classic theory. Although the theory of relativity is righter than Newton’s theory, I would not think about the world using the theory of relativity because that would make me crazy.  By the way, I do not think javascript is as great as the theory of relativity.

A simple alternative of the sleep function would be the following code:

function sleep(millis)
{
    var date = new Date();
    var curDate = null;
    do { curDate = new Date(); }
    while(curDate-date < millis);
}

But that is really a silly piece of code because it would occupy 100% CPU and freeze the web page. A smarter equivalent of the sleep function is:

function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

This function is actually an asynchronous function which returns(a Promise object) immediately. So how does it simulate the sleep behavior? Well, you can use the “await” operator before the calling of the  async function.

await sleep(2000);

Now this line will pause 2000 milliseconds then execute the following code. await, as the name implies, will wait for the returned Promise object to reach the fulfilled status. Now you should understand what the Promise object is.

When constructing a Promise object, you need to pass a function called executor as the parameter of the construction function. The function should have a resolve parameter and an optional reject parameter. The executor is called as soon as you new the Promise object. When the executor is called, it is passed two parameters by the system. The actual parameters are the system-defined resolve and reject function. I mean, you can use other names for the parameters when you define the executor like this:

function(myresolve,myreject)
{
    .....
    if(success)
      myresolve(0);
   else
      myreject(1);
}

But when the function is called, myresolve will be passed as the system-defined resolve and myreject will be passed as the system-defined reject. The system-defined resolve function will set the status of the Promise object from the initial pending status to fulfilled status. The system-defined reject function will change the status from the initial pending status to the rejected status. The status, after changed, will keep the same forever.

Now look back at our sleep function. The parameter passed when constructing the Promise object is not an ordinary function, but a lambda expression. A lamda expression essentially defines an anonymous function, the name before the arrow is the parameter(s) of the function. You should use brackets to enclose multiple parameters. If the function has no parameter, you should use a pair of brackets (containing nothing) in there. The stuff after the arrow is the function body. Here we call the setTimeout function to set the Promise object to the fulfilled status after specific time, in order for await to return.

That is not the whole story. In order to use await, you should include your code in an async function:

async function myfun()
{
  dosomething;
  await sleep(5000);//sleep 5 seconds
  do something else;
}

myfun();

When you call an async function like myfun, it will return a pending Promise object internally created by the async function immediately when it meets the await line.(note that the await expression itself evaluates to the value of the promise object following the await operator in the resolved status (at later time). This promise object is not the same as the promise object returned to the caller of this async function.) The current task(function) myfun will be put into the wait queue, and the code after the call of myfun will be executed. After all the subsequent code is executed, it will enter the event loop, where it will pick up the waiting task(myfun) and continue to execute the task. In the end, the async function returns with the promise object created earlier(when executing the await line) set to resolved status. A function cannot have two return values, right?

The Promise object returned from an async function is not the same as  that comes after the “return” statement. If you returns a resolved Promise object, the return value of the async function is another Promise object in pending status. The returned pending Promise object only get resolved when entering the event loop, after the Promise object coming after the “return” gets resolved. If you do not return a Promise in async function, the return value of the async function would be(or changed to) a fulfilled Promise with the result set to that value coming after the “return” statement. Keep in mind that all callback functions in the .then will be called when entering the event loop. Study the following example carefully, and you’ll understand what I’m saying:

 

<script>
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms,12));
}
var p2;
async function fun1()
{
     console.log("dosomething1");
     var p1=await sleep(3000);
     console.log(p1);
     console.log("dosomething2");
     //p2=new Promise(resolve=>resolve(12));
     p2=sleep(3000);
     p2.then(result=>console.log("p2:"+result));
     console.log(p2);
     console.log("returning");
     //return p2;
     return "returnfromfun1";
}
var prom;
console.log(prom=fun1());
console.log(p2);
console.log("hello:"+(p2==prom));
prom.then(result=>console.log("prom:"+result));
//p2.then(result=>console.log("p2:"+result));
console.log("hello1");
</script>

To summarize how async function works:

  • An async function will return when it meets the return statement or the await statement.
  • An async function always returns a Promise object created internally by itself(not you). In most cases, the returned Promise object is in pending status except when returning with a non-promise value(or just return;) where the returned promise object is in resolved status and the resolved value is set to the return value(or undefined for no return value). This is true even you return a resolved promise object created by yourself. If the returned promise is in pending status, when will it be resolved? It gets resolved when the promise object following the “return” gets resolved, or when the async function returns a non-promise value,  and in the event loop. And its resolved value is set to the resolved value of the  promise object following the “return”, or just the returned non-promise value. If the promise object following the “return” never gets resolved, the returned promise object will never get resolved too.
  • If there is an error or exception(such as use an undefined variable) during running the async function, the whole script will not stop. Instead, the code after the calling of the async function will continue and the promise returned from the async function will enter the rejected state, and .then function of the returned promise will be executed.
  • The function passed in Promise.then gets called only when entering the event loop.

If you understand how async/await work, you can know that if you put the sleep function in different async functions, you may not get  the expected result:

async function fun1()
{
     dosomething1;
     await sleep(1000);
     dosomething2;
}

async function fun2()
{
    dosomething3;
}

fun1();
fun2();

You want to dosomething1, wait 1000 ms, then dosomething2, then dosomething3 at last. But the result is dosomething1, then dosomething3, and dosomething2 at last.

 

Leave a Reply