0

In my code, I'm trying to put a certain delay before continuing to the rest of the code. Pretty basic. I'm using a custom sleep function because javascript's native sleep function is not working at all. I'm actually working in app script in google spreadsheets so maybe that's why. But the following code is in the <script> tag of the html file in spreadsheet app script.

Anyway, when I use sleep(), it is being executed before the setTimeout

function get_ids(db){

    window.alert("before window.ids is saved?");
    google.script.run.withSuccessHandler(getIdsFromAppscript).getIdsFromModel(db); 
    //this returns a result to getIdsFromAppscript but the following code doesn't wait 
    //for the result to be returned so I want to use sleep before the ids 
    //variable is returned by get_ids


    setTimeout(function(){
        window.alert("checking if ids is saved after 10s?");
        window.alert("In timeout ids="+window.ids);
        var ids= window.ids; //is non empty
    },10000);

    sleep(10000);

    var ids= window.ids;
    window.alert("after wait");
    window.alert("after sleep ids="+ids); //is empty but should be non empty

    return ids; //=window.ids , Need to return a non-empty result

}

function getIdsFromAppscript(result){
window.ids=result;

}

and the sleep function:

function sleep(ms) {
  var start = new Date().getTime(), expire = start + ms;
  while (new Date().getTime() < expire) { }
  return;
}

Current Order of output based on window.alert():

1) before window is saved?
2) after sleep
3) after sleep ids=  //basically empty which shouldn't be the case
4) checking if ids is saved after 10s?
5) In timeout ids= [.....] //non empty list

However, my desired output order is:

1) before window is saved?
2) checking if ids is saved after 10s?
3) In timeout ids= [.....] //non empty list
4) after sleep
5) after sleep ids= [...] //should be a non empty list 

The reason why I'm writing setTimeout is to show that after 10 seconds, the result is being stored in window.ids however even after I give a sleep for 10 seconds, or even 30 seconds, I can't get the result from window.ids.

What exactly am I doing wrong here? Thanks in advance~

Gemtastic
  • 6,253
  • 6
  • 34
  • 53
HackCode
  • 1,837
  • 6
  • 35
  • 66
  • 1
    I don't think using a `sleep` function is a great idea in Javascript. There's a reason if JS doesn't have a built-in function like that. Work **asynchronously** and forget about it, it's just gross and unnecessary. – Marco Bonelli Oct 19 '15 at 10:26
  • Please remember that Javascript execution is asynchronous, so `sleep(10000);` is indeed executed before the `setTimeOut` function is triggered. – Alex Oct 19 '15 at 10:30
  • getIdsFromAppscript will only be called once you have ids, so why not just use that? – Joey Ciechanowicz Oct 19 '15 at 10:31
  • @JoeyCiechanowicz I am using getIdsFromAppscript to save the result in a global variable. Then I'm trying to sleep/delay before actually being able to use the window.ids because window.ids is being filled only after 7-8 seconds. – HackCode Oct 19 '15 at 10:37
  • @Jaco I added the `setTimeOut` just to prove that after 10 seconds my `window.ids` is not null but still after sleeping for 10 seconds, it says null. That's my point. – HackCode Oct 19 '15 at 10:41
  • 1
    @HackCode Your sleep function will stop any Javascript execution, so regardless of the delay, the setTimeOut will be executed later, see this post for further details http://stackoverflow.com/questions/16873323/javascript-sleep-wait-before-continuing – Alex Oct 19 '15 at 10:50
  • @Jaco you're missing the point. Like I mentioned, my `setTimeOut` function is useless in terms of the output of my code. the only reason I added it is to show that after 10seconds I am indeed getting a non-empty result for window.ids but after sleeping for 10 seconds, window.ids is still empty. and my sleep function does work, like, it sleeps for 10s and then prints "after sleep". – HackCode Oct 19 '15 at 10:53
  • @HackCode, sorry for not being clearer. Your sleep code stops any Javascript execution, including the `setTimeout` function. It means that the `setTimeout` function will not be called until your `sleep` function has completed. So even if you would increase the sleep to 5 minutes, your `ids` array would still be empty – Alex Oct 19 '15 at 11:03
  • @Jaco I would disagree. Because, in my setTimeOut` I am just printing the value of the global variable. Had I not put the `setTimeOut` , the code's logic wouldn't change. – HackCode Oct 19 '15 at 11:08

3 Answers3

3

Java Script, especially through the V8 engine does not sleep. Sleeping causes the entire thread that JavaScript runs on to stop, which breaks the whole point of asynchronocy. setTimeout() only waits to run the function you push into it for the time you also put into it. It doesn't stop the rest of the executions, and whatever happens first will happen first.

If it's important to you that something happens in order, always, then you need to use callbacks or promises.

An example in code could be:

function doTimeout(ms) {
    setTimeout(function(){
      window.alert("checking if ids is saved after 10s?");
      window.alert("In timeout ids="+window.ids);
      var ids= window.ids; //is non empty
    },ms);
}

function sleep(ms, callback) {
  var start = new Date().getTime(), expire = start + ms;
  while (new Date().getTime() < expire) { }
  callback(ms);
}

sleep(10000, doTimeout);
Vogel612
  • 5,620
  • 5
  • 48
  • 73
Gemtastic
  • 6,253
  • 6
  • 34
  • 53
  • To be honest I think you need to read up on the fundamentals of JavaScript rather than just have a quick answer fitting your code. If the `setTimeout()` needs to happen after `sleep()` then make `sleep()` take two arguments; `ms` and a function executing the timeout. – Gemtastic Oct 19 '15 at 11:04
  • the `setTimeOut` is irrelevant to the code's logic. I am just printing the value of the global variable to check if it is returning an empty or non-empty list. It proves that after 10s, the `window.ids` is non-empty, and before that empty. But even after putting a sleep of 10s/20s/30s it still says empty. – HackCode Oct 19 '15 at 11:11
  • I used your code and it's still returning an empty result. :/ – HackCode Oct 19 '15 at 11:13
  • True, I might have misunderstood exactly what you wanted to do, you need to put the function you want run last at the end of the callback chain, however I recommend you to debug your code and start off by looking at what `db` gives you, to start with and then work your way through the chain. – Gemtastic Oct 19 '15 at 11:19
  • There's no problem with `db`. It's all good. I am getting the results. But just not at the time I want it. `setTimeOut` to 10 seconds and then checking `window.ids` gives me the output. But if I `sleep` for 10 seconds and then check `window.ids`, it gives a null output. This is very weird. – HackCode Oct 19 '15 at 11:32
1

Javascript is single threaded. You must return from your code for scripts in other threads to execute. Script in other threads includes functions to handle a timeout event, functions called when promises are kept or fail, and call back functions provided for asynchronous requests made using an XMLHttpRequest object.

Writing a function and calling it sleep() does not change this. You could have called it waitingForGodot() for all the difference it would make. What the code you provided does is to spend a lot of time looping in the thread it was called in. It does not relinquish control and blocks all other scripts from executing. If it goes on for long enough my browser will ask me if I wish to abort the (as in your) script.

traktor
  • 17,588
  • 4
  • 32
  • 53
0

I have included two examples below showing that your sleep function blocks the entire Javascript engine. When I use your sleep function, the interval function does not get executed even though I have set an interval of 100 ms and the output is delayed by 10 seconds. However, in the second example the output does get printed immediately at the correct interval. This shows your sleep function is blocking the entire execution engine and that explains why your ids array is empty.

function sleep(ms) {
  var start = new Date().getTime(),
    expire = start + ms;
  while (new Date().getTime() < expire) {}
  return;
}

function get_ids() {

  document.write("before window.ids is saved?" + "<br>");

  var counter = 0;


  setInterval(function() {
    while (counter < 100) {
      document.write("checking if ids is saved after 10s?" + "<br>");
      counter = counter + 1
    }
  }, 100);

  sleep(10000);

  documen.write("after wait");

}

document.write("Start");

get_ids()

document.write("End");

In this example I have commented out your sleep function and as expected the output gets printed every 100 ms:

function sleep(ms) {
  var start = new Date().getTime(),
    expire = start + ms;
  while (new Date().getTime() < expire) {}
  return;
}

function get_ids() {

  document.write("before window.ids is saved?" + "<br>");

  var counter = 0;

  setInterval(function() {
    while (counter < 100) {
      document.write("checking if ids is saved after 10s?" + "<br>");
      counter = counter + 1
    }
  }, 100);
  //sleep(10000);

  documen.write("after wait");

}

document.write("Start");

get_ids()

document.write("End");
Alex
  • 21,273
  • 10
  • 61
  • 73