I need to add a delay of about 100 miliseconds to my Javascript code but I don't want to use the setTimeout function of the window object and I don't want to use a busy loop. Does anyone have any suggestions?
Unfortunately, setTimeout() is the only reliable way (not the only way, but the only reliable way) to pause the execution of the script without blocking the UI.
It's not that hard to use actually, instead of writing this:
var x = 1;
// Place mysterious code that blocks the thread for 100 ms.
x = x * 3 + 2;
var y = x / 2;
you use setTimeout() to rewrite it this way:
var x = 1;
var y = null; // To keep under proper scope
setTimeout(function() {
x = x * 3 + 2;
y = x / 2;
}, 100);
I understand that using setTimeout() involves more thought than a desirable sleep() function, but unfortunately the later doesn't exist. Many workarounds are there to try to implement such functions. Some using busy loops:
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
others using an XMLHttpRequest tied with a server script that sleeps for a amount of time before returning a result.
Unfortunately, those are workarounds and are likely to cause other problems (such as freezing browsers). It is recommended to simply stick with the recommended way, which is setTimeout()).
If you're okay with ES2017, await is good:
const DEF_DELAY = 1000;
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms || DEF_DELAY));
}
await sleep(100);
Note that the await part needs to be in an async function:
//IIAFE (immediately invoked async function expression)
(async()=>{
//Do some stuff
await sleep(100);
//Do some more stuff
})()
I just had an issue where I needed to solve this properly.
Via Ajax, a script gets X (0-10) messages.
What I wanted to do:
Add one message to the DOM every 10 Seconds.
the code I ended up with:
$.each(messages, function(idx, el){
window.setTimeout(function(){
doSomething(el);
},Math.floor(idx+1)*10000);
});
Basically, think of the timeouts as a "timeline" of your script.
This is what we WANT to code:
DoSomething();
WaitAndDoNothing(5000);
DoSomethingOther();
WaitAndDoNothing(5000);
DoEvenMore();
This is HOW WE NEED TO TELL IT TO THE JAVASCRIPT:
At Runtime 0 : DoSomething();
At Runtime 5000 : DoSomethingOther();
At Runtime 10000: DoEvenMore();
Hope this helps.
This thread has a good discussion and a useful solution:
function pause( iMilliseconds )
{
var sDialogScript = 'window.setTimeout( function () { window.close(); }, ' + iMilliseconds + ');';
window.showModalDialog('javascript:document.writeln ("<script>' + sDialogScript + '<' + '/script>")');
}
Unfortunately it appears that this doesn't work in some versions of IE, but the thread has many other worthy proposals if that proves to be a problem for you.
Actually only setTimeout is fine for that job and normally you cannot set exact delays with non determined methods as busy loops.
Use a AJAX function which will call a php page synchronously and then in that page you can put the php usleep() function which will act as a delay.
function delay(t){
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp=new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.open("POST","http://www.hklabs.org/files/delay.php?time="+t,false);
//This will call the page named delay.php and the response will be sent to a division with ID as "response"
xmlhttp.send();
document.getElementById("response").innerHTML=xmlhttp.responseText;
}
http://www.hklabs.org/articles/put-delay-in-javascript
Related
I've been learning javascript looking through through openstack and need a little guidance, I've written the below code to open a website in chrome (my default browser), wait 5 seconds, and then refresh it 10 times using do/while loop.
Does it look OK as I want to run it from the terminal? I've been running this inside Chrome developer console and want to run it on my mac (10.9.5), is it a case of just running it through automator?
Many thanks for any help!
var vcount = 0;
function sleep(time) {
return new Promise((resolve) => setTimeout(resolve, time));
}
do {
window.location.assign("https://www.w3schools.com");
sleep(10000).then(() => {});
vcount++;
}
while (vcount < 10);
You need to use await to really pause the exectution:
(async function(){
do {
window.location.assign("https://www.w3schools.com");
await sleep(10000);
vcount++;
} while (vcount < 10);
})();
there is a lot of ways to do what you want, #JonasW gave one of them, here is an alternative:
function sleep(ms) {
const waitUntil = new Date().getTime() + ms;
while (new Date().getTime() < waitUntil) true;
}
This will run without await / Promise.
Edit
⚠️ Also, you'll need to pack up everything in a function, so it won't freeze the browser because JavaScript is running on a single thread:
setTimeout(function() {
/* Your code */
}, 0)
I'm receiving data in browser through websockets (paho-mqtt) but problem is that the receiving callback gets fired only when another task ends (big for loop) and it gets fired with all the stacked data, I'm not losing data just getting delayed. Shouldn't the callback get fired even if there is a loop running? What is happening here?. Otherwise, how can I achieve this, keep receiving while inside a loop?
What I'm trying to say is equivalent to the following:
If I do this in chrome
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < 50000; i++) {
console.log('for array');
}
I get
50000 VM15292:5 for array
VM15292:2 hello!
Shouldn't I get something like this?
1000 VM15292:5 for array
VM15292:2 hello!
49000 VM15292:5 for array
When you run JavaScript code in the browser (unless using Web Workers or other special technologies), it is executed on a single thread. That might not sound too important, but it is.
Your code consists of a for-loop (synchronous) and a call to setTimeout (asychronous). Since only one piece of JavaScript can be running at once, your for-loop will never be interrupted by setTimeout.
In fact, if your for-loop contained extremely intensive operations that required more than 10 ms to complete, your setTimeout callback might actually be delayed past that mark, because the browser always wait for the currently executing code to finish before continuing to run the event loop.
setTimeout(() => {
console.log('hello!');
}, 10);
for (var i = 0; i < /* 50000 */ 5; i++) {
console.log('for array');
}
The others have diagnosed the problem well, the single threaded nature of the browser. I will offer a possible solution: generators.
Here's a codepen which demonstrates the problem:
http://codepen.io/anon/pen/zZwXem?editors=1111
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 60000;
function log(message) {
const output = document.getElementById('output');
output.value = output.value + '\n' + message;
}
function asyncTask() {
log('Simulated websocket message')
}
function doWork() {
const timer = setInterval(1000, asyncTask);
let total = 0;
for (let i = 1; i < 100000000; i++) {
const foo = Math.log(i) * Math.sin(i);
total += foo;
}
log('The total is: '+ total);
clearInterval(timer);
}
When doWork() is called by clicking the 'Do Work' button, the asyncTask never runs, and the UI locks up. Horrible UX.
The following example uses a generator to run the long running task.
http://codepen.io/anon/pen/jBmoPZ?editors=1111
//Basically disable codepen infinite loop detection, which is faulty for generators
window.CP.PenTimer.MAX_TIME_IN_LOOP_WO_EXIT = 120000;
let workTimer;
function log(message) {
const output = document.getElementById('output');
output.value = output.value + '\n' + message;
}
function asyncTask() {
log('Simulated websocket message')
}
let workGenerator = null;
function runWork() {
if (workGenerator === null) {
workGenerator = doWork();
}
const work = workGenerator.next();
if (work.done) {
log('The total is: '+ work.value);
workerGenerator = null;
} else {
workTimer = setTimeout(runWork,0);
}
}
function* doWork() {
const timer = setInterval(asyncTask,1000);
let total = 0;
for (let i = 1; i < 100000000; i++) {
if (i % 100000 === 0) {
yield;
}
if (i % 1000000 == 0) {
log((i / 100000000 * 100).toFixed(1) + '% complete');
}
const foo = Math.log(i) * Math.sin(i);
total += foo;
}
clearInterval(timer);
return total;
}
Here we do work in a generator, and create a generator runner to call from the 'Do Work' button in the UI. This runs on the latest version of Chrome, I can't speak for other browsers. Typically you'd use something like babel to compile the generators down to ES5 syntax for a production build.
The generator yields every 10000 rows of calculation, and emits a status update every 100000 rows. The generator runner 'runWork' creates an instance of the generator and repeatedly calls next(). The generator then runs until it hits the next 'yield' or return statement. After the generator yields, the generator runner then gives up the UI thread by calling setTimeout with 0 milliseconds and using itself as the handler function. This typically means it will get called once every animation frame (ideally). This goes until the generator returns the done flag, at which point the generator runner can get the returned value and clean up.
Here the HTML for the example in case you need to recreate the codepen:
<input type='button' value='Do Work' onclick=doWork() />
<textarea id='output' style='width:200px;height:200px'></textarea>
Javascript engines tends to be single threaded.
So if you are in a long running tight loop that doesn't yield (e.g. to do some io) then the callback will never get a chance to run until the loop finishes
I have this function. Basically it searches data from database and does something with it. In this demo it just increases counter.
exports.fullThreads = function(){
return new Promise((resolve, reject) => {
MongoClient.connect(mongoUrl, (err, db) => {
var fullThreads = db.collection('tmp_fullThreads'),
threadPages = db.collection('tmp_threadPages').find();
var counter = 0;
threadPages.on('data', (doc) => {
setTimeout(function(){
counter++;
}, 200)
});
threadPages.on('end', () => {
console.log('end');
console.log(counter);
});
});//connect
});//promise
}//fullthreads
In this example I expected that it would call for data, wait 200ms, and then increase counter. Once it reaches the end, it would end. However it is different. on('end') is called before on('data') actually finished. I guess that is because it didn't return value immediately. So this setTimeout function is not a way to go. However I have a real problem here. This is simply way too fast and in real life code, this wouldn't increase counter, it would call foreign API which doesn't accept so many calls in so little time, so I wanted to make a delay between each call, how is this done properly?
Then you would need some kind of queu
eg:
queuTasks = [];
queuTasks.push("Task1");
queuTasks.push("Task2");
function doTasks(){
// Do your task
if(queuTasks.length > 0){
console.log(queuTasks[0]);
queuTasks.splice(0,1); // Remove it from the queu
}
setTimeout(function(){ doTasks();}, 1000);
}
This is just some quick code i made, may not work out of the box. But think you get the idea.
I know this is not entirely what you were asking about
This is my workaround:
var threadPages = db.collection('tmp_threadPages').find();
var delay = 0;
function test(doc, delay){
setTimeout(function(){
console.log(delay);
}, delay)
}
threadPages.on('data', (doc) => {
test(doc, delay);
delay += 100;
});
It works well if you do not need to bind proper events to on('end method. Basically it increases delay per request, otherwise it would fire all of them at once. This makes code go slower and it wouldn't overkill API with too many requests per second.
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~
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);
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.
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");
I read the Promise/A+ specification and it says under 2.2.4:
onFulfilled or onRejected must not be called until the execution context stack contains only platform code
But in Firefox (I tested 38.2.1 ESR and 40.0.3) the following script executes the onFulfilled method synchronously:
var p = Promise.resolve("Second");
p.then(alert);
alert("First");
(It does not seem to run using alerts here, it can also be tried here: http://jsbin.com/yovemaweye/1/edit?js,output)
It works as expected in other browsers or when using the ES6Promise-Polyfill.
Did I miss something here? I always though that one of the points of the then-method is to ensure asynchronous execution.
Edit:
It works when using console.log, see answer by Benjamin Gruenbaum:
function output(sMessage) {
console.log(sMessage);
}
var p = Promise.resolve("Second");
p.then(output);
output("First");
As he points out in the comments, this also happens when using synchronous requests, which is exactly why it happens in your test scenario.
I created a minimal example of what happens in our Tests:
function request(bAsync) {
return new Promise(function(resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function() {
if (xhr.readyState === XMLHttpRequest.DONE) {
resolve(xhr.responseText);
}
});
xhr.open("GET", "https://sapui5.hana.ondemand.com/sdk/resources/sap-ui-core.js", !!bAsync);
xhr.send();
});
}
function output(sMessage, bError) {
var oMessage = document.createElement("div");
if (bError) {
oMessage.style.color = "red";
}
oMessage.appendChild(document.createTextNode(sMessage));
document.body.appendChild(oMessage);
}
var sSyncData = null;
var sAsyncData = null;
request(true).then(function(sData) {
sAsyncData = sData;
output("Async data received");
});
request(false).then(function(sData) {
sSyncData = sData;
output("Sync data received");
});
// Tests
if (sSyncData === null) {
output("Sync data as expected");
} else {
output("Unexpected sync data", true);
}
if (sAsyncData === null) {
output("Async data as expected");
} else {
output("Unexpected async data", true);
}
In Firefox this leads to:
This is because you're using alert
When you use alert here it blocks and all bets are off - the page has frozen, execution halted and things are at "platform level".
It might be considered a bug, and it's certainly not what I would expect - but at the core this is about the incompatibility between alert and JavaScript task/microtask semantics.
If you change that alert to a console.log or appending to document.innerHTML you'd get the result you expect.
var alert = function(arg) { // no longer a magical and blocking operation
document.body.innerHTML += "<br/>" + arg;
}
// this code outputs "First, Second, Third" in all the browsers.
setTimeout(alert.bind(null, "Third"), 0);
var p = Promise.resolve("Second");
p.then(alert);
alert("First");
From what I can tell, this is actually permitted optional behavior:
Optionally, pause while waiting for the user to acknowledge the message.
(Emphasis mine)
Basically, what firefox does is this:
Execute until it encounters the first alert.
Run any microtasks to completion before pausing (tasks are paused, and not microtasks).
The then is run as a microtask, so "Second" gets queued and precedes the alert.
The Second alert gets run.
The First alert gets run.
Confusing, but allowed from what I can tell.