setTimeout in Node.js loop - javascript

I'm a bit confused as to how setTimeout works. I'm trying to have a setTimeout in a loop, so that the loop iterations are, say, 1s apart.
Each loop iteration makes an HTTP request and it seems like the server on the other end can't handle that many requests in such a short time span.
for (var i = 1; i<=2000 && ok; i++) {
var options = {
host:'www.host.com',
path:'/path/'+i
};
setTimeout(makeRequest(options, i), 1000);
};
Why does this not work and how can I achieve this?
Thank you

setTimeout is non blocking, it is asynchronous. You give it a callback and when the delay is over, your callback is called.
Here are some implementations:
Using recursion
You can use a recursive call in the setTimeout callback.
function waitAndDo(times) {
if(times < 1) {
return;
}
setTimeout(function() {
// Do something here
console.log('Doing a request');
waitAndDo(times-1);
}, 1000);
}
Here is how to use your function:
waitAndDo(2000); // Do it 2000 times
About stack overflow errors: setTimeout clear the call stack (see this question) so you don't have to worry about stack overflow on setTimeout recursive calls.
Using generators (io.js, ES6)
If you are already using io.js (the "next" Node.js that uses ES6) you can solve your problem without recursion with an elegant solution:
function* waitAndDo(times) {
for(var i=0; i<times; i++) {
// Sleep
yield function(callback) {
setTimeout(callback, 1000);
}
// Do something here
console.log('Doing a request');
}
}
Here is how to use your function (with co):
var co = require('co');
co(function* () {
yield waitAndDo(10);
});
BTW: This is really using a loop ;)
Generator functions documentation.

You need something like this
var counter = 5;
function makeRequst(options, i) {
// do your request here
}
function myFunction() {
alert(counter);
// create options object here
//var options = {
// host:'www.host.com',
// path:'/path/'+counter
//};
//makeRequest(options, counter);
counter--;
if (counter > 0) {
setTimeout(myFunction, 1000);
}
}
See also this fiddle
At the point of the alert(count); you can do your call to the server.
Note that the counter works opposite (counting down). I updated with some
comments where to do your thing.

Right now you're scheduling all of your requests to happen at the same time, just a second after the script runs. You'll need to do something like the following:
var numRequests = 2000,
cur = 1;
function scheduleRequest() {
if (cur > numRequests) return;
makeRequest({
host: 'www.host.com',
path: '/path/' + cur
}, cur);
cur++;
setTimeout(scheduleRequest, 1000)
}
Note that each subsequent request is only scheduled after the current one completes.

I might be late at the party but here is another (more readable) solution without the need to omit for loop.
What your code does is creating 2000 (actually 1999) setTimeout objects that will call the makeRequest function after 1 second from now. See, none of them knows about the existence of the other setTimeouts.
If you want them 1 sec apart from each other, you are responsible for creating them so.
This can be achieve by using your counter (in this case i) and the timeout delay.
for (var i = 1; i<=2000 && ok; i++) {
var options = {
host:'www.host.com',
path:'/path/'+i
};
setTimeout(makeRequest(options, i), i * 1000); //Note i * 1000
};
The first timeout object will be set for 1 second from now and the second one will be set for 2 seconds from now and so on; Meaning 1 second apart from each other.

I'm surprised that no one has mentioned this above, but it sounds like you need setInterval not setTimeout.
vat poller = setInterval(makeRequestFunc, 3000)
The code above will make a request every 3 seconds. Since you saved the object to the variable poller, you can stop polling by clearing the object like so:
cleanInterval(poller)

You're calling makeRequest() in your setTimeout call - you should be passing the function to setTimeout, not calling it, so something like
setTimeout(makeRequest, 1000);
without the ()

let i = 20;
let p = Promise.resolve(i)
while (i > 0) {
(i => {
p = p.then(() => {
return new Promise(function (resolve, reject) {
setTimeout(function () {
console.log(i);
resolve()
}, 2000)
})
})
})(i)
i--
}
p = p.then(data => console.log('execution ends'))

I'm very late on the subject (as usual ...;) but the only way I found to loop requests to a slow time response API and getting responses without HTTP 504 is using promises.
async function LoadDataFromAPI(myParametersArray) {
for(var i = 0; i < myParametersArray.length; i++) {
var x = await RunOneRequest(myParametersArray[i]);
console.log(x); // ok
}
}
The function called by the async function :
function RunOneRequest(parameter) {
return new Promise(resolve => {
setTimeout(() => {
request(parameter, (error, response, body) => {
// your request
});
resolve('ok);
}, 2000); // 2 secs
});
}

Related

Javascript: Loop through Array with Delay

I am trying to loop through an array, but want to output each value of the array with a delay. This is what my current understanding is on how it should work:
EDIT
Requested JS Fiddle: http://jsfiddle.net/d3whkjww/
loopThroughSplittedText: function(splittedText) {
for (var i = 0; i < splittedText.length; i++) {
// for each iteration console.log a word
// and make a pause after it
setTimeout(
console.log(splittedText[i]),
1000
);
};
},
Yet, it does not work, and I believe it might be, because the arguments in the "for" loop have to be inside the setTimeout function. Yet I don't know how to make it work.
All I get is every value of the array at once, but I want them appear with a delay. How do I do that?
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
function loopThroughSplittedText(splittedText) {
for (var i = 0; i < splittedText.length; i++) {
// for each iteration console.log a word
// and make a pause after it
(function (i) {
setTimeout(function () {
document.getElementById('text').innerHTML += splittedText[i];
console.log(splittedText[i]);
}, 1000 * i);
})(i);
};
}
loopThroughSplittedText(splittedText);
Fiddle Demo
Chances are you're going to want to use a recursive function instead of a for loop here. However, I'll explain both ways just in case you (or someone else reading this) has your heart set on doing this with a loop.
For a recursive function, the general idea is that you'll want to call the function once, then let it call itself repeatedly until it's finished doing what you want it to do. In terms of code, it will could look something a bit like this:
loopThroughSplittedText: function(splittedText) {
// Create our counter; delayedOutput will use this to
// track how far along in our string we are currently at
var locationInString = 0;
function delayedOutput() {
// Output the next letter in our string
console.log(splittedText[locationInString]);
// Increment our counter so that on the next call we are on the next letter
locationInString++;
// Only perform setTimeout if we still have text left to output
if (locationInString < splittedText.length) {
// Functions can reference themselves using their own name
setTimeout(delayedOutput, 1000);
}
}
// Call our function once to get things started
delayedOutput();
},
Alternatively, if you really prefer using a loop, you can still do it, but there's a fair bit of fiddling that has to be done to accomplish this.
First, you're going to need to place console.log within its own function. This is because when you place console.log(something), you're not actually passing it, but calling it right then and there, which is not what you want; by calling it, it spits out the text to the console right away rather than waiting until later. Tucking it away in its own function allows it to be passed to setTimeout so it can be called later on.
Second, you're going to have to wrap that function in yet another function to ensure that it's given the correct value of i when it fires. The reason is effectively this: Your intention is to tell the function "when you're ready, use what i was when I set you up." However, what you're doing right now is effectively saying "when you're ready, look at i". Because the function doesn't check what i is until it's ready to fire, it won't know its value until long after you have performed the loop, meaning i will be a number much higher than you want!
As a bit of a sub-point to the above, you'll want to call that function immediately. This is known as an immediately invoked function expression. If you're not familiar with them, they're certainly worth looking up. Their uses are a bit unusual, but they're a powerful tool in the right spot.
Finally, because you're setting up everything right here and now, you want to make sure the timeout for each function is a second apart; as it stands now, you're saying "do all of these one second from now", when your intention is "do all of these one second apart, starting one second from now". This fix is relatively easy; all you need to do is multiply your timeout by i so that you set up the first to go 1 second from now, the second to go 2 seconds from now, and so on.
All of that combined gives you code that looks something like this:
loopThroughSplittedText: function(splittedText) {
for (var i = 0; i < splittedText.length; i++) {
setTimeout(
(function(locationInString) {
return function() {
console.log(splittedText[locationInString]);
};
}(i)),
(1000*i)
);
}
},
As for which solution is better, I would probably recommend the recursive function. The recursive version will only create one function that calls itself for every string you pass it, whereas the for loop version will create one function for every character in the string, which could get out of hand very quickly. Function creation (and object creation in general) can get expensive in JavaScript when you're working on larger projects, so it's generally best to favor solutions that avoid creating massive amounts of functions when possible.
But still, for sake of explanation, I wouldn't want to leave you without the for loop version; the knowledge could come in handy in other places. :)
A recursive function call would do the job:
var a = [
1,2,3,4,5,6,7,8,9,10
];
function log(i){
console.log(a[i]);
if (i<a.length){
setTimeout(function(){
i++;
log(i);
},1000);
}
}
log(0);
http://jsfiddle.net/Curt/rjve4whe/1/
In my example, it will show you how to loop through an array contentiously until you stop. This is to just give you an idea on how you can do the delay. Also it shows you when the value actually got displayed.
I would say that you could actually create a nice utility from this timer, and use it for multiple purposes and with the utility it'll stop you from repeating large chunks of code.
JavaScript Loop example:
var body = document.body;
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
loopThroughArray(splittedText, function (arrayElement, loopTime) {
body.innerHTML += arrayElement+ ": " + loopTime+ "<br/>";
}, 1000);
function loopThroughArray(array, callback, interval) {
var newLoopTimer = new LoopTimer(function (time) {
var element = array.shift();
callback(element, time - start);
array.push(element);
}, interval);
var start = newLoopTimer.start();
};
// Timer
function LoopTimer(render, interval) {
var timeout;
var lastTime;
this.start = startLoop;
this.stop = stopLoop;
// Start Loop
function startLoop() {
timeout = setTimeout(createLoop, 0);
lastTime = Date.now();
return lastTime;
}
// Stop Loop
function stopLoop() {
clearTimeout(timeout);
return lastTime;
}
// The actual loop
function createLoop() {
var thisTime = Date.now();
var loopTime = thisTime - lastTime;
var delay = Math.max(interval - loopTime, 0);
timeout = setTimeout(createLoop, delay);
lastTime = thisTime + delay;
render(thisTime);
}
}
Ok, as It is not an exact duplicate, you need to increate the delay in the loop, also escape from the closure variable in a loop issue
loopThroughSplittedText: function (splittedText) {
splittedText.forEach(function (text, i) {
setTimeout(function () {
console.log(text);
}, i * 1000)
})
}
var obj = {
loopThroughSplittedText: function(splittedText) {
splittedText.forEach(function(text, i) {
setTimeout(function() {
document.getElementById('x').innerHTML += text
}, i * 1000)
})
}
}
obj.loopThroughSplittedText('abcde'.split(''))
<div id="x"></div>
One problem with your code is that i is common to all the callbacks. So the first callback is told "output the entry at index i", however by the time it gets to execute the initial loop is finished so i is now at the end of the text.
One way to achieve what you're looking for is to not use a for loop, but to have a function which (1) prints a character, (2) updates the counter/position, and (3) schedules the next character if needed:
loopThroughSplitText: function (text) {
var i = 0;
function printEntry() {
console.log(text[i]);
i++; // Increment the position
if (i < text.length) { // If there are more chars, schedule another
setTimeout(printEntry, 1000);
}
}
printEntry(); // Print the first entry/char
}
solution using closure
https://jsfiddle.net/x3azn/pan2oc9y/4/
function loopThroughSplittedText(splittedText) {
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
for (var i = 0; i < splittedText.length; i++) {
// for each iteration console.log a word
// and make a pause after it
(function(_i) {
setTimeout(function() {
window.document.getElementById('text').innerHTML = splittedText[_i];
console.log(splittedText[_i]);
}, 1000)
}(i));
}
}
loopThroughSplittedText()
One more solution, with a setInterval:
var i = 0;
var intv = setInterval(function() {
if (i >= splittedText.length) {
clearInterval(intv);
} else {
console.log(splittedText[i]);
++i;
}
}, 1000);
There are a couple of problems here
setTimeout should take a function, not the result of calling a function
setTimeout returns immediately, so all the actions in your loop will be started at roughly the same moment, and all wait 1000ms before execting (notwithstanding the comment above however, which means they're all executed at the same moment).
The value of i will all be equal to splittedText.length for each iteration due to not wrapping your loop control variable in a closure.
What you need to do, is wait until the setTimeout instructions are executed before moving on to the next iteration of the loop.
For example:
var splittedText = ["Hello", "World", "How", "Are", "You", "Today"];
function loopThroughSplittedText(splittedText) {
displayValue(splittedText,0);
}
function displayValue(arr, i){
if(i<arr.length){
setTimeout(function(){
document.getElementById('text').innerHTML = arr[i];
console.log(arr[i])
displayValue(arr,i+1);
},1000)
}
}
loopThroughSplittedText(splittedText)
Live example: http://jsfiddle.net/d3whkjww/1/
This will also work
function loopThroughSplittedText(splittedText) {
for (var i=0; i < splittedText.length;i++) {
(function(ind, text) {
setTimeout(function(){console.log(text);}, 1000 + (1000 * ind));
})(i, splittedText[i]);
}
}
Another sample:
var split = 'Lorem ipsum dolor'.split(' ');
var loop = function() {
console.log(split[0]);
split = split.slice(1);
if (split.length > 0) {
setTimeout(function() {
loop();
}, 1000);
}
}
loop();
Bringing out an alternative solution to the problem, which is making use of the third argument to setTimeout which is only supported in newer browsers:
(function (splittedText) {
for (var i = 0; i < splittedText.length; i++) {
setTimeout(
function(val) { console.log(val); },
i * 1000,
splittedText[i]
);
}
})(["Hello", "world", "!"]);
API documentation can be seen here (note the optional params).
You can achieve by 3 ways
1. closure
2. Recursive
3. variable declaration using let
var data = ['a', 'b', 'c', 'd'];
closure:
for(i=0; i<=data.length; i++) {
(function(x) {
setTimeout(() => {
console.log(x);
}, 1000)
})(data[i]);
}
let variable declaration
for(const ind of data) {
let local = ind;
setTimeout(() => {
console.log(local);
}, 1000)
}

Creating a "delay" function in JS?

I've been reading about setTimeout and other such timers. But I'm wondering if it's possible to work up a custom function so that all you would need to do is something like this:
//code
delay(time);
//more code
Is this possible?
UPDATE: Ok, I kind of get it. So if that isn't reasonably possible, how would you go about delaying a loop AFTER the first time. I want it to run immediately upon execution but they delay on each iteration afterward.
New UPDATE: I figure since my initial thought fell through, it might just be easier to show you the code I have.
function autoFarm (clickEvent){
var farmTargets = [
"6_300_1",
"6_300_3",
"6_300_4",
"6_300_5",
"6_300_7"];
setTimeout(function() {
$.each (farmTargets, function(index, target){
var extraData = '{"end_pos":"' + target + '","purpose":0,"upshift":1,"bring_res":{"0":0,"2":0,"1":0},"bring_ship":{"1":25,"11":0},"rate":100,"start_pos":"6_300_2"}';
var finalData = baseDataDora + extraData + "&type=1";
setTimeout(function(){
for (i = 0; i < farmTargets.length; i++){
postRequest(sendFleetURL + getSign(extraData). finalData, function(json){
});
}
}, 15000);
});//End each loop
}, 1320000);
}//End autoFarm
Basically, it should execute immediately and run the for loop 5 times on the first array element 15 seconds apart. Then 22 minutes later move to the next set and repeat for the entire array.
You can achieve something along those lines with generators. The idea is that continuation passing style (callback hell) can be flattened. The generator uses the yield keyword to pause the function, until the callback resumes it by calling its next method:
var async = function(gen) {
var g = gen()
function next(x) {
var cur = g.next(x)
if (cur.done) {
return cur.value
}
cur.value(next)
}
next()
}
var delay = function(time) {
return function(f) {
setTimeout(f, time)
}
}
async(function* () {
console.log('before')
yield delay(1000) // waits one second
console.log('middle')
yield delay(1000) // waits one second
console.log('after')
})
In CPS it would read something like:
console.log('before')
setTimeout(function() {
console.log('middle')
setTimeout(function() {
console.log('after')
}, 1000)
}, 1000)
This works in Chrome, Firefox and iojs today.
This isn't possible because of the way single-threaded event loops work. If this function were to exist, it would cause the entire UI thread to freeze until the delay was satisfied. setTimeout(cb, delay) is the nearest facility which schedules a function to be executed no earlier than the delay and at the end of the current event loop tick.
Update: Before somebody calls me on it, yes, you can theoretically engineer a delay function that freezes everything in place for a set amount of time. However, there is no reasonable excuse to do it this way.
To your second question:
function hello() {
console.log('hello');
}
// execute immediately
hello();
// then every 5 seconds
setInterval(hello, 5000);
As-written, no that's not possible.
If, instead you were to use a queue, delays in that manner are trivial.
jQuery's .queue() and .delay() functions are a good example of how this works, so I will use them as an example, however the general point stands for any queueing library.
Instead of:
//code
delay(time)
//more code
With a queue, you'd write:
$('...') //some selector to act on for jQuery
.queue(function (next) {
//code
//Indicate that the queued call is finished.
next();
//This allows async code to be executed in the queue,
//such as ajax and animations
})
.delay(time)
.queue(function (next) {
//more code
next();
});
Now, even if you ignore the lines used for comments, you can tell that there's a bit more boilerplate to achieve the desired behavior. I don't feel that it's excessive, because I find it relatively easy to read:
queue something to happen
wait for some number of milliseconds
queue something else to happen
Using a Promise, calling it inside an asynchronous function.
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const any_function = async() => {
await delay(2000);
console.log('this log has been delayed 2 secs')
}

Add a pause/interval to every iteration in a FOR LOOP

I want to write a for loop which prints number 1 to 10 with intervals after every iteration "like this"
How can I achieve it? I tried sleep() setInterval() setTimeout(") and what not but can't seem to find any working solution. And if possible I would like to do this using pure Javascript only.
function abc(){
for(i=1;i<=10;i++){
document.write(i+"<br>");
sleep(1000);
}
}
To answer the question, to get something like a sleep function you could just write somehting like this as a helper function
function sleep(dur) {
var d = new Date().getTime() + dur;
while(new Date().getTime() <= d ) {
//Do nothing
}
}
console.log(new Date().getTime())
sleep(1000)
console.log(new Date().getTime())
Then you could call the sleep function after every iteration like
function abc(){
for(i=1;i<=10;i++){
document.write(i+"<br>");
sleep(1000);
}
}
But Note that sleep will freeze your browser in this time and
you don't really wan't this kind of behaviour when you just want to periodiccally do sth
window.setInterval would be what you want in such cases
function abcd(i){
document.write(i + "<br>")
}
function repeatedTimeout(func,times,duration) {
var args = Array.prototype.slice.call(arguments).splice(3);
var i = 0;
args.push(i)
var wrap = function () {
if(args[args.length - 1] >= times)
window.clearInterval(wrap)
else {
func.apply(this,args)
args[args.length - 1]++
}
}
window.setInterval(wrap,duration)
}
repeatedTimeout(abcd,10,1000)
Which would call it 10 times every 1000 milliseconds, whithout freezing the Browers
Heres the JSBin
Update
If it really has to be a for loop, you could do something like this,
regardless of the sense it makes to me
for (var i = 0; i <= 10 ; i++) {
window.setTimeout(
(function (i){
return function() {
document.write(i + "<br>")
}
})(i),i * 1000)
}
In this case heres another JSBin
This would call window.setTimeout in a for loop and a multiple of the timeout with i as the timeout,
this would work, but i'd rather suggest using setInterval like you already did in the Fiddle you posted in the comment
Due to the mostly asynchronous (and single threaded) nature of JavaScript in the browser, constructs such as sleep() aren't the way to go.
You can write a generic function using setTimeout() that will do the looping and then pass in the function that should be run at every interval of x milliseconds. At least you'd have a reusable container in which you can run your code.
function loopn(n, fn, delay)
{
if (n > 0) {
fn();
if (n > 1) {
setTimeout(function() {
loopn(n - 1, fn, delay);
}, delay);
}
}
}
loopn(10, function() {
console.log('hello there');
}, 1000);
You could deconstruct the loop into a recursive function and use setTimeout to implement the pause.
var i = 0;
var limit = 10;
function loop(){
console.log(i);
i++;
if(i < limit)
{
setTimeout(loop, 100);
}
}
loop();
​

Synchronous delay in code execution

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!
Variation on the accepted answer which is just as good as this one.
Also, I agree with the caveats of preferring setTimeout and asynchronous function calling but sometimes e.g., when building tests, you just need a synchronous wait command...
function wait(ms) {
var start = Date.now(),
now = start;
while (now - start < ms) {
now = Date.now();
}
}
if you want it in seconds, divide start ms by 1000 on the while check...
=== EDIT ===
I noticed that my answer has bubbled to the top but it really shouldn't be the top answer. That was written as an alternative in case you cannot use async / await in your code or you're waiting for a trivial amount of time (like a second or two for testing).
The top answer should note that the async/await pattern is a much better way of doing this and will significantly use less energy and CPU cycles.
See #michaelolof 's answer below for example....
const wait = (msec) => new Promise((resolve, _) => {
setTimeout(resolve, msec));
});
(async () => {
console.log("Start...")
await wait(5000);
console.log("...End")
})();
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!")
JavaScript is a single-threaded language. You cannot combine setTimeout and synchronous processing. What will happen is, the timer will lapse, but then the JS engine will wait to process the results until the current script completes.
If you want synchronous methods, just call the method directly!
If you want to process something after the setTimeout, include it or call it from the timeout function.
Non-timeout loops (that check the time or count to 1000000 or whatever) just lock up the browser. setTimeout (or the $.doTimeout plugin) is the best way to do it.
Creating timeouts within a loop won't work because the loop doesn't wait for the previous timeout to occur before continuing, as you've discovered. Try something more like this:
// Generic function to execute a callback a given number
// of times with a given delay between each execution
function timeoutLoop(fn, reps, delay) {
if (reps > 0)
setTimeout(function() {
fn();
timeoutLoop(fn, reps-1, delay);
}, delay);
}
// pass your function as callback
timeoutLoop(function() { alert("Hi"); },
5,
5000);
(I just cobbled this together quickly, so although I'm confident that it works it could be improved in several ways, e.g., within the "loop" it could pass an index value into the callback function so that your own code knows which iteration it is up to. But hopefully it will get you started.)
I have made a simple synchronous timeout function. It works in two different ways, callback and non-callback.
function:
function wait(ms, cb) {
var waitDateOne = new Date();
while ((new Date()) - waitDateOne <= ms) {
//Nothing
}
if (cb) {
eval(cb);
}
}
callback example:
wait(5000,"doSomething();");
non-callback example:
console.log("Instant!");
wait(5000);
console.log("5 second delay");
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:
The event loop calls your script
Executes it line by line
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);
}
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>
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:
Occupy the thread by generating a uuid and looking for a non-existent file
New uuid each time defeats the file system cache
Looking for a file is likely an optimised operation that should allow other activity to continue (i.e. not pin the CPU)
Inspired by #andrew65952 but more modern-like and faster
function wait(ms) {
const now = Date.now()
while (Date.now() - now <= ms) { /* do nothing */}
}
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");

Create a pause inside a while loop in Javascript

I would like to create a pause inside a while loop so that I can create n animations that each appear 3 seconds after the other.
I've tried the following, but it doesn't work. Would love to have someone show me what I'm doing wrong.
i=0;
while (i < n) {
someanimation();
setTimeout(function(){
i++;
}, 3000);
};
setTimeout does not pause; it asks Javascript to run some other code later.
Googling for "setTimeout loop" tells you exactly what you need to know. If you look around a little bit, it even mentions setInterval. The difference: using setTimeout to loop will wait 3 seconds in between loops, whereas setInterval will make it take 3 seconds total for the loop (including however much time the animation takes, as long as it's less than 3 seconds :) ). Also, setInterval constructs an infinite loop that you'll have to break out of after the desired number of times; setTimeout requires you to construct the loop yourself.
i = 0;
// setTimeout approach
function animation_loop() {
someAnimation();
setTimeout(function() {
i++;
if (i < n) {
animation_loop();
}
}, 3000);
};
animation_loop();
// setInterval approach
i = 0;
someAnimation();
iid = setInterval(function() {
i++;
if (i < n) {
someAnimation();
} else {
clearInterval(iid);
}
}, 3000);
setTimeout is a little trickier than that because it doesn't block (i.e. it doesn't finish waiting on the timeout before continuing with the program).
What you want is closer to this:
var i = 0;
function nextFrame() {
if(i < n) {
someanimation();
i++;
// Continue the loop in 3s
setTimeout(nextFrame, 3000);
}
}
// Start the loop
setTimeout(nextFrame, 0);
It may also be worth your while to read up on setInterval as a possible alternative.
Well, thanks to ES6-7 with Promises we can now make a pause and make it look nice at the same time!
var id = 0;
async function do() {
while(true) {
await pause(id);
//will happen only after pause is done
id++;
}
}
function pause(id) {
return new Promise(resolve => setTimeout(() => {
console.log(`pause ${id} is over`);
resolve();
}, 1500));
}
do();
One of the way of doing it is to use RxJS. Please take a look at working example here
Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(x))
create a function like:
function sleep_until (seconds) {
var max_sec = new Date().getTime();
while (new Date() < max_sec + seconds * 1000) {}
return true;
}
and then change your code to
i=0;
while (i < n) {
someanimation();
sleep_until(3);
do_someotheranimation();
};
You are not very specific about what you want to do, but I'd say the main problem is that you call someanimation() without a delay. So maybe this solves it for you:
for (var i = 0; i < n; i++) {
setTimeout(someanimation, 3000 * i);
};
Note the missing () after someanimation as it is the callback for setTimeout().
function myFunction() {
var x;
for(var i=0;i<10;i++){
if (confirm("Press a button!") == true) {
x = "You pressed OK!";
} else {
x = "You pressed Cancel!";
}
document.getElementById("demo").innerHTML = x;
}
}``

Categories

Resources