The differences of "setInterval" between chrome and other browsers, Why? - javascript

CODE 1
console.log('start');
const interval = setInterval(() => {
console.log('setInterval');
}, 0);
setTimeout(() => {
console.log('setTimeout 1');
Promise.resolve()
.then(() => {
console.log('promise 3');
})
.then(() => {
console.log('promise 4');
})
.then(() => {
setTimeout(() => {
console.log('setTimeout 2');
Promise.resolve()
.then(() => {
console.log('promise 5');
})
.then(() => {
console.log('promise 6');
})
.then(() => {
clearInterval(interval);
});
}, 0);
});
}, 0);
Promise.resolve()
.then(() => {
console.log('promise 1');
})
.then(() => {
console.log('promise 2');
});
The result of this code on Windows Edge/Mac Safari/Opera browsers is as follow:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setTimeout 2
promise 5
promise 6
But when ran on chrome on Windows or Mac, the result have two cases.
Sometimes the result is same as above.
Sometimes the result outputs two setInterval as follow:
start
promise 1
promise 2
setInterval
setTimeout 1
promise 3
promise 4
setInterval
setInterval
setTimeout 2
promise 5
promise 6
I'm confused about the result.
CODE 2
function expendTime(k){
console.log((new Date()).getTime());
while(k < 10000){
for(var j = 2; j < k; j++){
if(k%j == 0){
break;
}
if(j == k-1){
console.log(k)
}
}
k++;
}
console.log((new Date()).getTime());
}
var t = setInterval(expendTime,15,3);
setTimeout(function() {
clearInterval(t);
},30);
.as-console-wrapper {
max-height: 100% !important;
}
The result of this code when ran on chrome and others browsers is different.
I consider the result on other browsers is correct, since it is coincides with my common sense.

Yes chrome's* implementation of setInterval does self-correct itself so its calls do fire at exact intervals, removing any drift.
*and maybe Edge's one ?
Here is an example code which does implement such a drift-correcting setInterval method, ran in chrome, you can see it has the same effect than the builtin one, while other implementations and recursive setTimeout will keep growing the drift.
// drift-correcting setInterval
// returns an object which "_id" property is the inner timeout id, so it can be canceled by clearInterval
function driftCorrectingInterval(cb, delay) {
var begin = performance.now(), // what time is it?
result = {
_id: setTimeout(inner, delay)
},
passed = true; // a flag to avoid try-catch the callback
return result;
function inner() {
if (!passed) return; // 'cb' thrown
passed = false; // set up the callback trap
var now = performance.now(),
drift = (now - begin) - delay;
begin += delay; // start a new interval
result._id = setTimeout(inner, delay - drift);
// call it at the end so we can cancel inside the callback
cb();
passed = true; // didn't throw we can continue
}
}
// snippet-only tests
function test(ms) {
function setTimeoutLoop(cb, ms) {
function loop() {
cb();
setTimeout(loop, ms);
}
setTimeout(loop, ms);
}
var now = performance.now(),
built_in_prev = now,
timeout_prev = now,
sCI_prev = now,
built_in_elem = document.querySelector('#test_' + ms + ' .delay.built_in'),
timeout_elem = document.querySelector('#test_' + ms + ' .delay.timeout'),
sCI_elem = document.querySelector('#test_' + ms + ' .delay.sCI');
setInterval(() => {
var now = performance.now(),
delay = (now - built_in_prev) - ms;
built_in_prev += ms;
built_in_elem.textContent = Math.round(delay);
}, ms);
setTimeoutLoop(() => {
var now = performance.now(),
delay = (now - timeout_prev) - ms;
timeout_prev += ms;
timeout_elem.textContent = Math.round(delay);
}, ms);
driftCorrectingInterval(() => {
var now = performance.now(),
delay = (now - sCI_prev) - ms;
sCI_prev += ms;
sCI_elem.textContent = Math.round(delay);
}, ms);
}
test(1000);
[id^='test'] {
border: 1px solid;
padding: 0 12px
}
<div id="test_1000">
<p>built in setInterval drift: <span class="delay built_in">0</span>ms</p>
<p>built in setTimeout loop drift: <span class="delay timeout">0</span>ms</p>
<p>driftCorrectingInterval drift: <span class="delay sCI">0</span>ms</p>
</div>
Note that current specs read as Firefox's implementation, and as what you understood, i.e a recursive setTimeout, without any consideration for drift.
Indeed the timer initialization steps requires that when the repeat flag is set to true, the same arguments should be passed to the next timer initialization steps method.
But an open issue discusses this very problem and might lead to a revision of the specs so that UAs are encouraged to apply such drift-correction when possible.

Related

For a setTimeout in a for loop, why is necessary to multiply the iterating value(i) to the timer, and upon multiplying its not working as expected [duplicate]

I would like to add a delay/sleep inside a while loop:
I tried it like this:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}
Only the first scenario is true: after showing alert('hi'), it will be waiting for 3 seconds then alert('hello') will be displayed but then alert('hello') will be repeatedly constantly.
What I would like is that after alert('hello') is shown 3 seconds after alert('hi') then it needs to wait for 3 seconds for the second time alert('hello') and so on.
The setTimeout() function is non-blocking and will return immediately. Therefore your loop will iterate very quickly and it will initiate 3-second timeout triggers one after the other in quick succession. That is why your first alerts pops up after 3 seconds, and all the rest follow in succession without any delay.
You may want to use something like this instead:
var i = 1; // set your counter to 1
function myLoop() { // create a loop function
setTimeout(function() { // call a 3s setTimeout when the loop is called
console.log('hello'); // your code here
i++; // increment the counter
if (i < 10) { // if the counter < 10, call the loop function
myLoop(); // .. again which will trigger another
} // .. setTimeout()
}, 3000)
}
myLoop(); // start the loop
You could also neaten it up, by using a self invoking function, passing the number of iterations as an argument:
(function myLoop(i) {
setTimeout(function() {
console.log('hello'); // your code here
if (--i) myLoop(i); // decrement i and call myLoop again if i > 0
}, 3000)
})(10); // pass the number of iterations as an argument
Since ES7 theres a better way to await a loop:
// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => new Promise(res => setTimeout(res, ms))
async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i < 3; i++) {
console.log(i);
await timer(3000); // then the created Promise can be awaited
}
}
load();
When the engine reaches the await part, it sets a timeout and halts the execution of the async function. Then when the timeout completes, execution continues at that point. That's quite useful as you can delay (1) nested loops, (2) conditionally, (3) nested functions:
async function task(i) { // 3
await timer(1000);
console.log(`Task ${i} done!`);
}
async function main() {
for(let i = 0; i < 100; i+= 10) {
for(let j = 0; j < 10; j++) { // 1
if(j % 2) { // 2
await task(i + j);
}
}
}
}
main();
function timer(ms) { return new Promise(res => setTimeout(res, ms)); }
Reference on MDN
While ES7 is now supported by NodeJS and modern browsers, you might want to transpile it with BabelJS so that it runs everywhere.
If using ES6, you could use a for loop to achieve this:
for (let i = 1; i < 10; i++) {
setTimeout(function timer() {
console.log("hello world");
}, i * 3000);
}
It declares i for each iteration, meaning the timeout is what it was before + 1000. This way, what is passed to setTimeout is exactly what we want.
Try something like this:
var i = 0, howManyTimes = 10;
function f() {
console.log("hi");
i++;
if (i < howManyTimes) {
setTimeout(f, 3000);
}
}
f();
Another way is to multiply the time to timeout, but note that this is not like sleep. Code after the loop will be executed immediately, only the execution of the callback function is deferred.
for (var start = 1; start < 10; start++)
setTimeout(function () { alert('hello'); }, 3000 * start);
The first timeout will be set to 3000 * 1, the second to 3000 * 2 and so on.
This will work
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}
Try this fiddle: https://jsfiddle.net/wgdx8zqq/
I think you need something like this:
var TimedQueue = function(defaultDelay){
this.queue = [];
this.index = 0;
this.defaultDelay = defaultDelay || 3000;
};
TimedQueue.prototype = {
add: function(fn, delay){
this.queue.push({
fn: fn,
delay: delay
});
},
run: function(index){
(index || index === 0) && (this.index = index);
this.next();
},
next: function(){
var self = this
, i = this.index++
, at = this.queue[i]
, next = this.queue[this.index]
if(!at) return;
at.fn();
next && setTimeout(function(){
self.next();
}, next.delay||this.defaultDelay);
},
reset: function(){
this.index = 0;
}
}
Test code:
var now = +new Date();
var x = new TimedQueue(2000);
x.add(function(){
console.log('hey');
console.log(+new Date() - now);
});
x.add(function(){
console.log('ho');
console.log(+new Date() - now);
}, 3000);
x.add(function(){
console.log('bye');
console.log(+new Date() - now);
});
x.run();
Note: using alerts stalls javascript execution till you close the alert.
It might be more code than you asked for, but this is a robust reusable solution.
You can create a sleep function that promisifies setTimeout. This enables you to use async/await to write code without callbacks and the familiar for loop control flow.
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
(async () => {
for (let i = 0; i < 10; i++) {
console.log(i);
await sleep(1000);
}
console.log("done");
})();
In Node, you can use timers/promises to avoid the promisification step (if the feature isn't supported on your older Node version, the above code works just as well):
const {setTimeout: sleep} = require("timers/promises");
// same code as above
Regardless, since JS is single-threaded, it's a good thing that timeouts are asynchronous. If they weren't, the browser wouldn't get a chance to repaint the UI, leading to a frozen interface for the user.
I would probably use setInterval, like this:
var period = 1000; // ms
var endTime = 10000; // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
alert('Hello');
if(counter === endTime){
clearInterval(sleepyAlert);
}
counter += period;
}, period);
In my opinion, the simpler and most elegant way to add a delay in a loop is like this:
names = ['John', 'Ana', 'Mary'];
names.forEach((name, i) => {
setTimeout(() => {
console.log(name);
}, i * 1000); // one sec interval
});
In ES6 (ECMAScript 2015) you can iterate with delay with generator and interval.
Generators, a new feature of ECMAScript 6, are functions that can be
paused and resumed. Calling genFunc does not execute it. Instead, it
returns a so-called generator object that lets us control genFunc’s
execution. genFunc() is initially suspended at the beginning of its
body. The method genObj.next() continues the execution of genFunc,
until the next yield.
(Exploring ES6)
Code example:
let arr = [1, 2, 3, 'b'];
let genObj = genFunc();
let val = genObj.next();
console.log(val.value);
let interval = setInterval(() => {
val = genObj.next();
if (val.done) {
clearInterval(interval);
} else {
console.log(val.value);
}
}, 1000);
function* genFunc() {
for(let item of arr) {
yield item;
}
}
So if you are using ES6, that the most elegant way to achieve loop with delay (for my opinion).
I do this with Bluebird’s Promise.delay and recursion.
function myLoop(i) {
return Promise.delay(1000)
.then(function() {
if (i > 0) {
alert('hello');
return myLoop(i -= 1);
}
});
}
myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>
Dead-simple, one-line solution with an actual async-await delay (no queued setTimeout):
The following (self-executing anonymous) function creates an actual delay between loops, instead of multiple setTimeouts with different timeouts, which might mess up memory.
In each of the 100 loops, it awaits for a new Promise to resolve.
This happens only after setTimeout 'allows' it after 90ms. Until then, code is blocked by the async-await / pending Promise.
(async () => {
for (let i=0; i<100; i++) {
await new Promise((resolve) => {setTimeout(() => {document.write(`${i} `); resolve(true)}, 90)});
}
})()
In ES6 you can do as following:
for (let i = 0; i <= 10; i++){
setTimeout(function () {
console.log(i);
}, i*3000)
}
In ES5 you can do as:
for (var i = 0; i <= 10; i++){
(function(i) {
setTimeout(function () {
console.log(i);
}, i*3000)
})(i);
}
The reason is, let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
A function-less solution
I am a bit late to the party, but there is a solution without using any functions:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(() => alert('hello'), 3000 * start);
}
Just thought I'd post my two cents here as well. This function runs an iterative loop with a delay. See this jsfiddle. The function is as follows:
function timeout(range, time, callback){
var i = range[0];
callback(i);
Loop();
function Loop(){
setTimeout(function(){
i++;
if (i<range[1]){
callback(i);
Loop();
}
}, time*1000)
}
}
For example:
//This function prints the loop number every second
timeout([0, 5], 1, function(i){
console.log(i);
});
Would be equivalent to:
//This function prints the loop number instantly
for (var i = 0; i<5; i++){
console.log(i);
}
To my knowledge the setTimeout function is called asynchronously. What you can do is wrap the entire loop within an async function and await a Promise that contains the setTimeout as shown:
var looper = async function () {
for (var start = 1; start < 10; start++) {
await new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("iteration: " + start.toString());
resolve(true);
}, 1000);
});
}
return true;
}
And then you call run it like so:
looper().then(function(){
console.log("DONE!")
});
Please take some time to get a good understanding of asynchronous programming.
In addition to the accepted answer from 10 years ago, with more modern Javascript one can use async/await/Promise() or generator function to achieve the correct behavior. (The incorrect behavior suggested in other answers would be setting series of 3 seconds alerts regardless of "accepting" the alert() - or finishing the task at hand)
Using async/await/Promise():
alert('hi');
(async () => {
for(let start = 1; start < 10; start++) {
await new Promise(resolve => setTimeout(() => {
alert('hello');
resolve();
}, 3000));
}
})();
Using a generator function:
alert('hi');
let func;
(func = (function*() {
for(let start = 1; start < 10; start++) {
yield setTimeout(() => {
alert('hello');
func.next();
}, 3000);
}
})()).next();
You can use the RxJS interval operator. interval emits an integer every x seconds, and take specifies the number of times it emits these numbers.
Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 1000;
function functionToRun(i, length) {
alert(data[i]);
}
(function forWithDelay(i, length, fn, delay) {
setTimeout(function() {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
A modified version of Daniel Vassallo's answer, with variables extracted into parameters to make the function more reusable:
First let's define some essential variables:
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;
Next you should define the function you want to run. This will get passed i, the current index of the loop and the length of the loop, in case you need it:
function functionToRun(i, length) {
alert(data[i]);
}
Self-executing version
(function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
Functional version
function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
}
forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it
Just try this
var arr = ['A','B','C'];
(function customLoop (arr, i) {
setTimeout(function () {
// Do here what you want to do.......
console.log(arr[i]);
if (--i) {
customLoop(arr, i);
}
}, 2000);
})(arr, arr.length);
Result
A // after 2s
B // after 2s
C // after 2s
This script works for most things
function timer(start) {
setTimeout(function () { //The timer
alert('hello');
}, start*3000); //needs the "start*" or else all the timers will run at 3000ms
}
for(var start = 1; start < 10; start++) {
timer(start);
}
Here is how I created an infinite loop with a delay that breaks on a certain condition:
// Now continuously check the app status until it's completed,
// failed or times out. The isFinished() will throw exception if
// there is a failure.
while (true) {
let status = await this.api.getStatus(appId);
if (isFinished(status)) {
break;
} else {
// Delay before running the next loop iteration:
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
The key here is to create a new Promise that resolves by timeout, and to await for its resolution.
Obviously you need async/await support for that. Works in Node 8.
for common use "forget normal loops" and use this combination of "setInterval" includes "setTimeOut"s: like this (from my real tasks).
function iAsk(lvl){
var i=0;
var intr =setInterval(function(){ // start the loop
i++; // increment it
if(i>lvl){ // check if the end round reached.
clearInterval(intr);
return;
}
setTimeout(function(){
$(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
},50);
setTimeout(function(){
// do another bla bla bla after 100 millisecond.
seq[i-1]=(Math.ceil(Math.random()*4)).toString();
$("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
$("#d"+seq[i-1]).prop("src",pGif);
var d =document.getElementById('aud');
d.play();
},100);
setTimeout(function(){
// keep adding bla bla bla till you done :)
$("#d"+seq[i-1]).prop("src",pPng);
},900);
},1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
}
PS: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.
PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..
let counter =1;
for(let item in items) {
counter++;
setTimeout(()=>{
//your code
},counter*5000); //5Sec delay between each iteration
}
const autoPlayer = (arr = [1, 2, 3, 4, 5]) => {
// Base case:
if (arr.length < 1) return
// Remove the first element from the array.
const item = arr.shift()
// Set timout
setTimeout(() => {
console.log('Hello, world!', item) // Visualisation.
autoPlayer() // Call function again.
}, 1000) // Iterate every second.
}
Hey, I know this post is very old, but this code "loops" and adds a delay to it using a recursive method. I don't think you can 'actually' delay a loop itself from iterating based on reading various comments from other people. Maybe this can help someone out! Basically the function accepts an array (in this example). On each iteration the setTimeout Javascript method is called. The function calls itself again indefinitely when the timer of the setTimeout function expires, but on each call the array becomes smaller until it reaches the base-case. I hope this can help anyone else out.
/*
Use Recursive and setTimeout
call below function will run loop loopFunctionNeedCheck until
conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay
reRunAfterMs miliseconds and continue loop
tested code, thanks
*/
function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
loopFunctionNeedCheck) {
loopFunctionNeedCheck();
var result = conditionCheckAfterRunFn();
//check after run
if (!result) {
setTimeout(function () {
functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
}, reRunAfterMs);
}
else console.log("completed, thanks");
//if you need call a function after completed add code call callback in here
}
//passing-parameters-to-a-callback-function
// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function () {
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function () {
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
//test code:
var result = 0;
console.log("---> init result is " + result);
var functionNeedRun = function (step) {
result+=step;
console.log("current result is " + result);
}
var checkResultFunction = function () {
return result==100;
}
//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));
//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/
Here is a function that I use for looping over an array:
function loopOnArrayWithDelay(theArray, delayAmount, i, theFunction, onComplete){
if (i < theArray.length && typeof delayAmount == 'number'){
console.log("i "+i);
theFunction(theArray[i], i);
setTimeout(function(){
loopOnArrayWithDelay(theArray, delayAmount, (i+1), theFunction, onComplete)}, delayAmount);
}else{
onComplete(i);
}
}
You use it like this:
loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
//Do something with item
}, function(i){
//Do something once loop has completed
}
<!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Try it</button>
<p id="demo"></p>
<script>
function myFunction() {
for(var i=0; i<5; i++) {
var sno = i+1;
(function myLoop (i) {
setTimeout(function () {
alert(i); // Do your function here
}, 1000*i);
})(sno);
}
}
</script>
</body>
</html>
Try this...
var icount=0;
for (let i in items) {
icount=icount+1000;
new beginCount(items[i],icount);
}
function beginCount(item,icount){
setTimeout(function () {
new actualFunction(item,icount);
}, icount);
}
function actualFunction(item,icount){
//...runs ever 1 second
console.log(icount);
}

JavaScript execute event each every time

I have this:
let time = store.time;
const run = setInterval(() => {
const delay = store.delay;
setTimeout(() => {
console.log("Execute event....");
}, delay * 1000);
time--;
if (time === 0) {
clearInterval(run);
}
}, 1000);
I just want to run a process indicating a time and delaying the time at the same time, for example:
time=10 ; delay=2, then the process will be executed when the time is equal to: 2, 4, 6, 8 and 10
time=10 ; delay=3, then the process will be executed when the time is equal to: 3, 6 and 9
time=10 ; delay=4, then the process will be executed when the time is equal to: 4 and 8
time=10 ; delay=5, then the process will be executed when the time is equal to: 5 and 10
It's not currently working that way, it's only running 10 times, why??
If you use a promise and async/await you can use a normal for loop to log the steps at the right intervals.
// Return a promise that resolves after a
// certain duration
function delay(step) {
return new Promise(res => {
setTimeout(() => res(), step * 1000);
});
}
// Accepts a limit, and a step count
async function loop(limit, step) {
// Log the first step and then...
console.log(step);
// ...loop over the rest of the numbers calling
// the delay function on each iteration
for (let i = step + step; i <= limit; i += step) {
await delay(step);
console.log(i);
}
}
const limit = 10;
const step = 2;
loop(limit, step);
You don't need to use both setInterval() and setTimeout(). Use setInterval(), with the interval being the delay converted to milliseconds. At each repetition, subtract the delay from the total number of times, and when this reaches the end, stop the timer.
let time = 10;
let delay = 3;
const run = setInterval(() => {
console.log("Execute event....");
time -= delay;
if (time < delay) {
console.log("Done");
clearInterval(run);
}
}, delay * 1000);

setTimeout in nested for loops in JavaScript not working like what I want [duplicate]

I would like to add a delay/sleep inside a while loop:
I tried it like this:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}
Only the first scenario is true: after showing alert('hi'), it will be waiting for 3 seconds then alert('hello') will be displayed but then alert('hello') will be repeatedly constantly.
What I would like is that after alert('hello') is shown 3 seconds after alert('hi') then it needs to wait for 3 seconds for the second time alert('hello') and so on.
The setTimeout() function is non-blocking and will return immediately. Therefore your loop will iterate very quickly and it will initiate 3-second timeout triggers one after the other in quick succession. That is why your first alerts pops up after 3 seconds, and all the rest follow in succession without any delay.
You may want to use something like this instead:
var i = 1; // set your counter to 1
function myLoop() { // create a loop function
setTimeout(function() { // call a 3s setTimeout when the loop is called
console.log('hello'); // your code here
i++; // increment the counter
if (i < 10) { // if the counter < 10, call the loop function
myLoop(); // .. again which will trigger another
} // .. setTimeout()
}, 3000)
}
myLoop(); // start the loop
You could also neaten it up, by using a self invoking function, passing the number of iterations as an argument:
(function myLoop(i) {
setTimeout(function() {
console.log('hello'); // your code here
if (--i) myLoop(i); // decrement i and call myLoop again if i > 0
}, 3000)
})(10); // pass the number of iterations as an argument
Since ES7 theres a better way to await a loop:
// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => new Promise(res => setTimeout(res, ms))
async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i < 3; i++) {
console.log(i);
await timer(3000); // then the created Promise can be awaited
}
}
load();
When the engine reaches the await part, it sets a timeout and halts the execution of the async function. Then when the timeout completes, execution continues at that point. That's quite useful as you can delay (1) nested loops, (2) conditionally, (3) nested functions:
async function task(i) { // 3
await timer(1000);
console.log(`Task ${i} done!`);
}
async function main() {
for(let i = 0; i < 100; i+= 10) {
for(let j = 0; j < 10; j++) { // 1
if(j % 2) { // 2
await task(i + j);
}
}
}
}
main();
function timer(ms) { return new Promise(res => setTimeout(res, ms)); }
Reference on MDN
While ES7 is now supported by NodeJS and modern browsers, you might want to transpile it with BabelJS so that it runs everywhere.
If using ES6, you could use a for loop to achieve this:
for (let i = 1; i < 10; i++) {
setTimeout(function timer() {
console.log("hello world");
}, i * 3000);
}
It declares i for each iteration, meaning the timeout is what it was before + 1000. This way, what is passed to setTimeout is exactly what we want.
Try something like this:
var i = 0, howManyTimes = 10;
function f() {
console.log("hi");
i++;
if (i < howManyTimes) {
setTimeout(f, 3000);
}
}
f();
Another way is to multiply the time to timeout, but note that this is not like sleep. Code after the loop will be executed immediately, only the execution of the callback function is deferred.
for (var start = 1; start < 10; start++)
setTimeout(function () { alert('hello'); }, 3000 * start);
The first timeout will be set to 3000 * 1, the second to 3000 * 2 and so on.
This will work
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}
Try this fiddle: https://jsfiddle.net/wgdx8zqq/
I think you need something like this:
var TimedQueue = function(defaultDelay){
this.queue = [];
this.index = 0;
this.defaultDelay = defaultDelay || 3000;
};
TimedQueue.prototype = {
add: function(fn, delay){
this.queue.push({
fn: fn,
delay: delay
});
},
run: function(index){
(index || index === 0) && (this.index = index);
this.next();
},
next: function(){
var self = this
, i = this.index++
, at = this.queue[i]
, next = this.queue[this.index]
if(!at) return;
at.fn();
next && setTimeout(function(){
self.next();
}, next.delay||this.defaultDelay);
},
reset: function(){
this.index = 0;
}
}
Test code:
var now = +new Date();
var x = new TimedQueue(2000);
x.add(function(){
console.log('hey');
console.log(+new Date() - now);
});
x.add(function(){
console.log('ho');
console.log(+new Date() - now);
}, 3000);
x.add(function(){
console.log('bye');
console.log(+new Date() - now);
});
x.run();
Note: using alerts stalls javascript execution till you close the alert.
It might be more code than you asked for, but this is a robust reusable solution.
You can create a sleep function that promisifies setTimeout. This enables you to use async/await to write code without callbacks and the familiar for loop control flow.
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
(async () => {
for (let i = 0; i < 10; i++) {
console.log(i);
await sleep(1000);
}
console.log("done");
})();
In Node, you can use timers/promises to avoid the promisification step (if the feature isn't supported on your older Node version, the above code works just as well):
const {setTimeout: sleep} = require("timers/promises");
// same code as above
Regardless, since JS is single-threaded, it's a good thing that timeouts are asynchronous. If they weren't, the browser wouldn't get a chance to repaint the UI, leading to a frozen interface for the user.
I would probably use setInterval, like this:
var period = 1000; // ms
var endTime = 10000; // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
alert('Hello');
if(counter === endTime){
clearInterval(sleepyAlert);
}
counter += period;
}, period);
In my opinion, the simpler and most elegant way to add a delay in a loop is like this:
names = ['John', 'Ana', 'Mary'];
names.forEach((name, i) => {
setTimeout(() => {
console.log(name);
}, i * 1000); // one sec interval
});
In ES6 (ECMAScript 2015) you can iterate with delay with generator and interval.
Generators, a new feature of ECMAScript 6, are functions that can be
paused and resumed. Calling genFunc does not execute it. Instead, it
returns a so-called generator object that lets us control genFunc’s
execution. genFunc() is initially suspended at the beginning of its
body. The method genObj.next() continues the execution of genFunc,
until the next yield.
(Exploring ES6)
Code example:
let arr = [1, 2, 3, 'b'];
let genObj = genFunc();
let val = genObj.next();
console.log(val.value);
let interval = setInterval(() => {
val = genObj.next();
if (val.done) {
clearInterval(interval);
} else {
console.log(val.value);
}
}, 1000);
function* genFunc() {
for(let item of arr) {
yield item;
}
}
So if you are using ES6, that the most elegant way to achieve loop with delay (for my opinion).
I do this with Bluebird’s Promise.delay and recursion.
function myLoop(i) {
return Promise.delay(1000)
.then(function() {
if (i > 0) {
alert('hello');
return myLoop(i -= 1);
}
});
}
myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>
Dead-simple, one-line solution with an actual async-await delay (no queued setTimeout):
The following (self-executing anonymous) function creates an actual delay between loops, instead of multiple setTimeouts with different timeouts, which might mess up memory.
In each of the 100 loops, it awaits for a new Promise to resolve.
This happens only after setTimeout 'allows' it after 90ms. Until then, code is blocked by the async-await / pending Promise.
(async () => {
for (let i=0; i<100; i++) {
await new Promise((resolve) => {setTimeout(() => {document.write(`${i} `); resolve(true)}, 90)});
}
})()
In ES6 you can do as following:
for (let i = 0; i <= 10; i++){
setTimeout(function () {
console.log(i);
}, i*3000)
}
In ES5 you can do as:
for (var i = 0; i <= 10; i++){
(function(i) {
setTimeout(function () {
console.log(i);
}, i*3000)
})(i);
}
The reason is, let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
A function-less solution
I am a bit late to the party, but there is a solution without using any functions:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(() => alert('hello'), 3000 * start);
}
Just thought I'd post my two cents here as well. This function runs an iterative loop with a delay. See this jsfiddle. The function is as follows:
function timeout(range, time, callback){
var i = range[0];
callback(i);
Loop();
function Loop(){
setTimeout(function(){
i++;
if (i<range[1]){
callback(i);
Loop();
}
}, time*1000)
}
}
For example:
//This function prints the loop number every second
timeout([0, 5], 1, function(i){
console.log(i);
});
Would be equivalent to:
//This function prints the loop number instantly
for (var i = 0; i<5; i++){
console.log(i);
}
To my knowledge the setTimeout function is called asynchronously. What you can do is wrap the entire loop within an async function and await a Promise that contains the setTimeout as shown:
var looper = async function () {
for (var start = 1; start < 10; start++) {
await new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("iteration: " + start.toString());
resolve(true);
}, 1000);
});
}
return true;
}
And then you call run it like so:
looper().then(function(){
console.log("DONE!")
});
Please take some time to get a good understanding of asynchronous programming.
In addition to the accepted answer from 10 years ago, with more modern Javascript one can use async/await/Promise() or generator function to achieve the correct behavior. (The incorrect behavior suggested in other answers would be setting series of 3 seconds alerts regardless of "accepting" the alert() - or finishing the task at hand)
Using async/await/Promise():
alert('hi');
(async () => {
for(let start = 1; start < 10; start++) {
await new Promise(resolve => setTimeout(() => {
alert('hello');
resolve();
}, 3000));
}
})();
Using a generator function:
alert('hi');
let func;
(func = (function*() {
for(let start = 1; start < 10; start++) {
yield setTimeout(() => {
alert('hello');
func.next();
}, 3000);
}
})()).next();
You can use the RxJS interval operator. interval emits an integer every x seconds, and take specifies the number of times it emits these numbers.
Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 1000;
function functionToRun(i, length) {
alert(data[i]);
}
(function forWithDelay(i, length, fn, delay) {
setTimeout(function() {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
A modified version of Daniel Vassallo's answer, with variables extracted into parameters to make the function more reusable:
First let's define some essential variables:
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;
Next you should define the function you want to run. This will get passed i, the current index of the loop and the length of the loop, in case you need it:
function functionToRun(i, length) {
alert(data[i]);
}
Self-executing version
(function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
Functional version
function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
}
forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it
Just try this
var arr = ['A','B','C'];
(function customLoop (arr, i) {
setTimeout(function () {
// Do here what you want to do.......
console.log(arr[i]);
if (--i) {
customLoop(arr, i);
}
}, 2000);
})(arr, arr.length);
Result
A // after 2s
B // after 2s
C // after 2s
This script works for most things
function timer(start) {
setTimeout(function () { //The timer
alert('hello');
}, start*3000); //needs the "start*" or else all the timers will run at 3000ms
}
for(var start = 1; start < 10; start++) {
timer(start);
}
Here is how I created an infinite loop with a delay that breaks on a certain condition:
// Now continuously check the app status until it's completed,
// failed or times out. The isFinished() will throw exception if
// there is a failure.
while (true) {
let status = await this.api.getStatus(appId);
if (isFinished(status)) {
break;
} else {
// Delay before running the next loop iteration:
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
The key here is to create a new Promise that resolves by timeout, and to await for its resolution.
Obviously you need async/await support for that. Works in Node 8.
for common use "forget normal loops" and use this combination of "setInterval" includes "setTimeOut"s: like this (from my real tasks).
function iAsk(lvl){
var i=0;
var intr =setInterval(function(){ // start the loop
i++; // increment it
if(i>lvl){ // check if the end round reached.
clearInterval(intr);
return;
}
setTimeout(function(){
$(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
},50);
setTimeout(function(){
// do another bla bla bla after 100 millisecond.
seq[i-1]=(Math.ceil(Math.random()*4)).toString();
$("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
$("#d"+seq[i-1]).prop("src",pGif);
var d =document.getElementById('aud');
d.play();
},100);
setTimeout(function(){
// keep adding bla bla bla till you done :)
$("#d"+seq[i-1]).prop("src",pPng);
},900);
},1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
}
PS: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.
PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..
let counter =1;
for(let item in items) {
counter++;
setTimeout(()=>{
//your code
},counter*5000); //5Sec delay between each iteration
}
const autoPlayer = (arr = [1, 2, 3, 4, 5]) => {
// Base case:
if (arr.length < 1) return
// Remove the first element from the array.
const item = arr.shift()
// Set timout
setTimeout(() => {
console.log('Hello, world!', item) // Visualisation.
autoPlayer() // Call function again.
}, 1000) // Iterate every second.
}
Hey, I know this post is very old, but this code "loops" and adds a delay to it using a recursive method. I don't think you can 'actually' delay a loop itself from iterating based on reading various comments from other people. Maybe this can help someone out! Basically the function accepts an array (in this example). On each iteration the setTimeout Javascript method is called. The function calls itself again indefinitely when the timer of the setTimeout function expires, but on each call the array becomes smaller until it reaches the base-case. I hope this can help anyone else out.
/*
Use Recursive and setTimeout
call below function will run loop loopFunctionNeedCheck until
conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay
reRunAfterMs miliseconds and continue loop
tested code, thanks
*/
function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
loopFunctionNeedCheck) {
loopFunctionNeedCheck();
var result = conditionCheckAfterRunFn();
//check after run
if (!result) {
setTimeout(function () {
functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
}, reRunAfterMs);
}
else console.log("completed, thanks");
//if you need call a function after completed add code call callback in here
}
//passing-parameters-to-a-callback-function
// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function () {
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function () {
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
//test code:
var result = 0;
console.log("---> init result is " + result);
var functionNeedRun = function (step) {
result+=step;
console.log("current result is " + result);
}
var checkResultFunction = function () {
return result==100;
}
//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));
//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/
Here is a function that I use for looping over an array:
function loopOnArrayWithDelay(theArray, delayAmount, i, theFunction, onComplete){
if (i < theArray.length && typeof delayAmount == 'number'){
console.log("i "+i);
theFunction(theArray[i], i);
setTimeout(function(){
loopOnArrayWithDelay(theArray, delayAmount, (i+1), theFunction, onComplete)}, delayAmount);
}else{
onComplete(i);
}
}
You use it like this:
loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
//Do something with item
}, function(i){
//Do something once loop has completed
}
<!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Try it</button>
<p id="demo"></p>
<script>
function myFunction() {
for(var i=0; i<5; i++) {
var sno = i+1;
(function myLoop (i) {
setTimeout(function () {
alert(i); // Do your function here
}, 1000*i);
})(sno);
}
}
</script>
</body>
</html>
Try this...
var icount=0;
for (let i in items) {
icount=icount+1000;
new beginCount(items[i],icount);
}
function beginCount(item,icount){
setTimeout(function () {
new actualFunction(item,icount);
}, icount);
}
function actualFunction(item,icount){
//...runs ever 1 second
console.log(icount);
}

Javascript Fetch Api Data and show each item [duplicate]

I would like to add a delay/sleep inside a while loop:
I tried it like this:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(function () {
alert('hello');
}, 3000);
}
Only the first scenario is true: after showing alert('hi'), it will be waiting for 3 seconds then alert('hello') will be displayed but then alert('hello') will be repeatedly constantly.
What I would like is that after alert('hello') is shown 3 seconds after alert('hi') then it needs to wait for 3 seconds for the second time alert('hello') and so on.
The setTimeout() function is non-blocking and will return immediately. Therefore your loop will iterate very quickly and it will initiate 3-second timeout triggers one after the other in quick succession. That is why your first alerts pops up after 3 seconds, and all the rest follow in succession without any delay.
You may want to use something like this instead:
var i = 1; // set your counter to 1
function myLoop() { // create a loop function
setTimeout(function() { // call a 3s setTimeout when the loop is called
console.log('hello'); // your code here
i++; // increment the counter
if (i < 10) { // if the counter < 10, call the loop function
myLoop(); // .. again which will trigger another
} // .. setTimeout()
}, 3000)
}
myLoop(); // start the loop
You could also neaten it up, by using a self invoking function, passing the number of iterations as an argument:
(function myLoop(i) {
setTimeout(function() {
console.log('hello'); // your code here
if (--i) myLoop(i); // decrement i and call myLoop again if i > 0
}, 3000)
})(10); // pass the number of iterations as an argument
Since ES7 theres a better way to await a loop:
// Returns a Promise that resolves after "ms" Milliseconds
const timer = ms => new Promise(res => setTimeout(res, ms))
async function load () { // We need to wrap the loop into an async function for this to work
for (var i = 0; i < 3; i++) {
console.log(i);
await timer(3000); // then the created Promise can be awaited
}
}
load();
When the engine reaches the await part, it sets a timeout and halts the execution of the async function. Then when the timeout completes, execution continues at that point. That's quite useful as you can delay (1) nested loops, (2) conditionally, (3) nested functions:
async function task(i) { // 3
await timer(1000);
console.log(`Task ${i} done!`);
}
async function main() {
for(let i = 0; i < 100; i+= 10) {
for(let j = 0; j < 10; j++) { // 1
if(j % 2) { // 2
await task(i + j);
}
}
}
}
main();
function timer(ms) { return new Promise(res => setTimeout(res, ms)); }
Reference on MDN
While ES7 is now supported by NodeJS and modern browsers, you might want to transpile it with BabelJS so that it runs everywhere.
If using ES6, you could use a for loop to achieve this:
for (let i = 1; i < 10; i++) {
setTimeout(function timer() {
console.log("hello world");
}, i * 3000);
}
It declares i for each iteration, meaning the timeout is what it was before + 1000. This way, what is passed to setTimeout is exactly what we want.
Try something like this:
var i = 0, howManyTimes = 10;
function f() {
console.log("hi");
i++;
if (i < howManyTimes) {
setTimeout(f, 3000);
}
}
f();
Another way is to multiply the time to timeout, but note that this is not like sleep. Code after the loop will be executed immediately, only the execution of the callback function is deferred.
for (var start = 1; start < 10; start++)
setTimeout(function () { alert('hello'); }, 3000 * start);
The first timeout will be set to 3000 * 1, the second to 3000 * 2 and so on.
This will work
for (var i = 0; i < 10; i++) {
(function(i) {
setTimeout(function() { console.log(i); }, 100 * i);
})(i);
}
Try this fiddle: https://jsfiddle.net/wgdx8zqq/
I think you need something like this:
var TimedQueue = function(defaultDelay){
this.queue = [];
this.index = 0;
this.defaultDelay = defaultDelay || 3000;
};
TimedQueue.prototype = {
add: function(fn, delay){
this.queue.push({
fn: fn,
delay: delay
});
},
run: function(index){
(index || index === 0) && (this.index = index);
this.next();
},
next: function(){
var self = this
, i = this.index++
, at = this.queue[i]
, next = this.queue[this.index]
if(!at) return;
at.fn();
next && setTimeout(function(){
self.next();
}, next.delay||this.defaultDelay);
},
reset: function(){
this.index = 0;
}
}
Test code:
var now = +new Date();
var x = new TimedQueue(2000);
x.add(function(){
console.log('hey');
console.log(+new Date() - now);
});
x.add(function(){
console.log('ho');
console.log(+new Date() - now);
}, 3000);
x.add(function(){
console.log('bye');
console.log(+new Date() - now);
});
x.run();
Note: using alerts stalls javascript execution till you close the alert.
It might be more code than you asked for, but this is a robust reusable solution.
You can create a sleep function that promisifies setTimeout. This enables you to use async/await to write code without callbacks and the familiar for loop control flow.
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
(async () => {
for (let i = 0; i < 10; i++) {
console.log(i);
await sleep(1000);
}
console.log("done");
})();
In Node, you can use timers/promises to avoid the promisification step (if the feature isn't supported on your older Node version, the above code works just as well):
const {setTimeout: sleep} = require("timers/promises");
// same code as above
Regardless, since JS is single-threaded, it's a good thing that timeouts are asynchronous. If they weren't, the browser wouldn't get a chance to repaint the UI, leading to a frozen interface for the user.
I would probably use setInterval, like this:
var period = 1000; // ms
var endTime = 10000; // ms
var counter = 0;
var sleepyAlert = setInterval(function(){
alert('Hello');
if(counter === endTime){
clearInterval(sleepyAlert);
}
counter += period;
}, period);
In my opinion, the simpler and most elegant way to add a delay in a loop is like this:
names = ['John', 'Ana', 'Mary'];
names.forEach((name, i) => {
setTimeout(() => {
console.log(name);
}, i * 1000); // one sec interval
});
In ES6 (ECMAScript 2015) you can iterate with delay with generator and interval.
Generators, a new feature of ECMAScript 6, are functions that can be
paused and resumed. Calling genFunc does not execute it. Instead, it
returns a so-called generator object that lets us control genFunc’s
execution. genFunc() is initially suspended at the beginning of its
body. The method genObj.next() continues the execution of genFunc,
until the next yield.
(Exploring ES6)
Code example:
let arr = [1, 2, 3, 'b'];
let genObj = genFunc();
let val = genObj.next();
console.log(val.value);
let interval = setInterval(() => {
val = genObj.next();
if (val.done) {
clearInterval(interval);
} else {
console.log(val.value);
}
}, 1000);
function* genFunc() {
for(let item of arr) {
yield item;
}
}
So if you are using ES6, that the most elegant way to achieve loop with delay (for my opinion).
I do this with Bluebird’s Promise.delay and recursion.
function myLoop(i) {
return Promise.delay(1000)
.then(function() {
if (i > 0) {
alert('hello');
return myLoop(i -= 1);
}
});
}
myLoop(3);
<script src="//cdnjs.cloudflare.com/ajax/libs/bluebird/2.9.4/bluebird.min.js"></script>
Dead-simple, one-line solution with an actual async-await delay (no queued setTimeout):
The following (self-executing anonymous) function creates an actual delay between loops, instead of multiple setTimeouts with different timeouts, which might mess up memory.
In each of the 100 loops, it awaits for a new Promise to resolve.
This happens only after setTimeout 'allows' it after 90ms. Until then, code is blocked by the async-await / pending Promise.
(async () => {
for (let i=0; i<100; i++) {
await new Promise((resolve) => {setTimeout(() => {document.write(`${i} `); resolve(true)}, 90)});
}
})()
In ES6 you can do as following:
for (let i = 0; i <= 10; i++){
setTimeout(function () {
console.log(i);
}, i*3000)
}
In ES5 you can do as:
for (var i = 0; i <= 10; i++){
(function(i) {
setTimeout(function () {
console.log(i);
}, i*3000)
})(i);
}
The reason is, let allows you to declare variables that are limited to a scope of a block statement, or expression on which it is used, unlike the var keyword, which defines a variable globally, or locally to an entire function regardless of block scope.
A function-less solution
I am a bit late to the party, but there is a solution without using any functions:
alert('hi');
for(var start = 1; start < 10; start++) {
setTimeout(() => alert('hello'), 3000 * start);
}
Just thought I'd post my two cents here as well. This function runs an iterative loop with a delay. See this jsfiddle. The function is as follows:
function timeout(range, time, callback){
var i = range[0];
callback(i);
Loop();
function Loop(){
setTimeout(function(){
i++;
if (i<range[1]){
callback(i);
Loop();
}
}, time*1000)
}
}
For example:
//This function prints the loop number every second
timeout([0, 5], 1, function(i){
console.log(i);
});
Would be equivalent to:
//This function prints the loop number instantly
for (var i = 0; i<5; i++){
console.log(i);
}
To my knowledge the setTimeout function is called asynchronously. What you can do is wrap the entire loop within an async function and await a Promise that contains the setTimeout as shown:
var looper = async function () {
for (var start = 1; start < 10; start++) {
await new Promise(function (resolve, reject) {
setTimeout(function () {
console.log("iteration: " + start.toString());
resolve(true);
}, 1000);
});
}
return true;
}
And then you call run it like so:
looper().then(function(){
console.log("DONE!")
});
Please take some time to get a good understanding of asynchronous programming.
In addition to the accepted answer from 10 years ago, with more modern Javascript one can use async/await/Promise() or generator function to achieve the correct behavior. (The incorrect behavior suggested in other answers would be setting series of 3 seconds alerts regardless of "accepting" the alert() - or finishing the task at hand)
Using async/await/Promise():
alert('hi');
(async () => {
for(let start = 1; start < 10; start++) {
await new Promise(resolve => setTimeout(() => {
alert('hello');
resolve();
}, 3000));
}
})();
Using a generator function:
alert('hi');
let func;
(func = (function*() {
for(let start = 1; start < 10; start++) {
yield setTimeout(() => {
alert('hello');
func.next();
}, 3000);
}
})()).next();
You can use the RxJS interval operator. interval emits an integer every x seconds, and take specifies the number of times it emits these numbers.
Rx.Observable
.interval(1000)
.take(10)
.subscribe((x) => console.log(x))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.lite.min.js"></script>
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 1000;
function functionToRun(i, length) {
alert(data[i]);
}
(function forWithDelay(i, length, fn, delay) {
setTimeout(function() {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
A modified version of Daniel Vassallo's answer, with variables extracted into parameters to make the function more reusable:
First let's define some essential variables:
var startIndex = 0;
var data = [1, 2, 3];
var timeout = 3000;
Next you should define the function you want to run. This will get passed i, the current index of the loop and the length of the loop, in case you need it:
function functionToRun(i, length) {
alert(data[i]);
}
Self-executing version
(function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
})(startIndex, data.length, functionToRun, timeout);
Functional version
function forWithDelay(i, length, fn, delay) {
setTimeout(function () {
fn(i, length);
i++;
if (i < length) {
forWithDelay(i, length, fn, delay);
}
}, delay);
}
forWithDelay(startIndex, data.length, functionToRun, timeout); // Lets run it
Just try this
var arr = ['A','B','C'];
(function customLoop (arr, i) {
setTimeout(function () {
// Do here what you want to do.......
console.log(arr[i]);
if (--i) {
customLoop(arr, i);
}
}, 2000);
})(arr, arr.length);
Result
A // after 2s
B // after 2s
C // after 2s
This script works for most things
function timer(start) {
setTimeout(function () { //The timer
alert('hello');
}, start*3000); //needs the "start*" or else all the timers will run at 3000ms
}
for(var start = 1; start < 10; start++) {
timer(start);
}
Here is how I created an infinite loop with a delay that breaks on a certain condition:
// Now continuously check the app status until it's completed,
// failed or times out. The isFinished() will throw exception if
// there is a failure.
while (true) {
let status = await this.api.getStatus(appId);
if (isFinished(status)) {
break;
} else {
// Delay before running the next loop iteration:
await new Promise(resolve => setTimeout(resolve, 3000));
}
}
The key here is to create a new Promise that resolves by timeout, and to await for its resolution.
Obviously you need async/await support for that. Works in Node 8.
for common use "forget normal loops" and use this combination of "setInterval" includes "setTimeOut"s: like this (from my real tasks).
function iAsk(lvl){
var i=0;
var intr =setInterval(function(){ // start the loop
i++; // increment it
if(i>lvl){ // check if the end round reached.
clearInterval(intr);
return;
}
setTimeout(function(){
$(".imag").prop("src",pPng); // do first bla bla bla after 50 millisecond
},50);
setTimeout(function(){
// do another bla bla bla after 100 millisecond.
seq[i-1]=(Math.ceil(Math.random()*4)).toString();
$("#hh").after('<br>'+i + ' : rand= '+(Math.ceil(Math.random()*4)).toString()+' > '+seq[i-1]);
$("#d"+seq[i-1]).prop("src",pGif);
var d =document.getElementById('aud');
d.play();
},100);
setTimeout(function(){
// keep adding bla bla bla till you done :)
$("#d"+seq[i-1]).prop("src",pPng);
},900);
},1000); // loop waiting time must be >= 900 (biggest timeOut for inside actions)
}
PS: Understand that the real behavior of (setTimeOut): they all will start in same time "the three bla bla bla will start counting down in the same moment" so make a different timeout to arrange the execution.
PS 2: the example for timing loop, but for a reaction loops you can use events, promise async await ..
let counter =1;
for(let item in items) {
counter++;
setTimeout(()=>{
//your code
},counter*5000); //5Sec delay between each iteration
}
const autoPlayer = (arr = [1, 2, 3, 4, 5]) => {
// Base case:
if (arr.length < 1) return
// Remove the first element from the array.
const item = arr.shift()
// Set timout
setTimeout(() => {
console.log('Hello, world!', item) // Visualisation.
autoPlayer() // Call function again.
}, 1000) // Iterate every second.
}
Hey, I know this post is very old, but this code "loops" and adds a delay to it using a recursive method. I don't think you can 'actually' delay a loop itself from iterating based on reading various comments from other people. Maybe this can help someone out! Basically the function accepts an array (in this example). On each iteration the setTimeout Javascript method is called. The function calls itself again indefinitely when the timer of the setTimeout function expires, but on each call the array becomes smaller until it reaches the base-case. I hope this can help anyone else out.
/*
Use Recursive and setTimeout
call below function will run loop loopFunctionNeedCheck until
conditionCheckAfterRunFn = true, if conditionCheckAfterRunFn == false : delay
reRunAfterMs miliseconds and continue loop
tested code, thanks
*/
function functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn,
loopFunctionNeedCheck) {
loopFunctionNeedCheck();
var result = conditionCheckAfterRunFn();
//check after run
if (!result) {
setTimeout(function () {
functionRepeatUntilConditionTrue(reRunAfterMs, conditionCheckAfterRunFn, loopFunctionNeedCheck)
}, reRunAfterMs);
}
else console.log("completed, thanks");
//if you need call a function after completed add code call callback in here
}
//passing-parameters-to-a-callback-function
// From Prototype.js
if (!Function.prototype.bind) { // check if native implementation available
Function.prototype.bind = function () {
var fn = this, args = Array.prototype.slice.call(arguments),
object = args.shift();
return function () {
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
}
//test code:
var result = 0;
console.log("---> init result is " + result);
var functionNeedRun = function (step) {
result+=step;
console.log("current result is " + result);
}
var checkResultFunction = function () {
return result==100;
}
//call this function will run loop functionNeedRun and delay 500 miliseconds until result=100
functionRepeatUntilConditionTrue(500, checkResultFunction , functionNeedRun.bind(null, 5));
//result log from console:
/*
---> init result is 0
current result is 5
undefined
current result is 10
current result is 15
current result is 20
current result is 25
current result is 30
current result is 35
current result is 40
current result is 45
current result is 50
current result is 55
current result is 60
current result is 65
current result is 70
current result is 75
current result is 80
current result is 85
current result is 90
current result is 95
current result is 100
completed, thanks
*/
Here is a function that I use for looping over an array:
function loopOnArrayWithDelay(theArray, delayAmount, i, theFunction, onComplete){
if (i < theArray.length && typeof delayAmount == 'number'){
console.log("i "+i);
theFunction(theArray[i], i);
setTimeout(function(){
loopOnArrayWithDelay(theArray, delayAmount, (i+1), theFunction, onComplete)}, delayAmount);
}else{
onComplete(i);
}
}
You use it like this:
loopOnArrayWithDelay(YourArray, 1000, 0, function(e, i){
//Do something with item
}, function(i){
//Do something once loop has completed
}
<!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Try it</button>
<p id="demo"></p>
<script>
function myFunction() {
for(var i=0; i<5; i++) {
var sno = i+1;
(function myLoop (i) {
setTimeout(function () {
alert(i); // Do your function here
}, 1000*i);
})(sno);
}
}
</script>
</body>
</html>
Try this...
var icount=0;
for (let i in items) {
icount=icount+1000;
new beginCount(items[i],icount);
}
function beginCount(item,icount){
setTimeout(function () {
new actualFunction(item,icount);
}, icount);
}
function actualFunction(item,icount){
//...runs ever 1 second
console.log(icount);
}

setTimeout() being called immediately in throttle function

so I'm trying to write a throttle function that when called, calls a callback but only up to a certain limit number of times in a given interval. If the limit is reached, the callback gets pushed into a queue where the callback gets called after the initial interval.
const throttle = (cb, interval, maxCalls) => {
let calls = 0;
const records = [];
let totalCalls = 0;
return (...rest) => {
if(calls < maxCalls) {
calls++;
records.push(Date.now());
totalCalls++;
cb.apply(null, rest);
setTimeout(() => {
calls--;
}, interval);
} else {
//cb within setTimeout being invoked immediately here
setTimeout(() => {
calls++;
records.push(Date.now());
totalCalls++;
cb.apply(null, rest);
//console.log(allotedTime: interval - (Date.now() - records[(totalCalls-1)-maxCalls]));
}, interval - (Date.now() - records[(totalCalls-1)-maxCalls]));
}
}
}
const meow = (start, ...args) => {
console.log(Date.now() - start, ...args);
}
const burp = throttle(meow.bind(this, Date.now()), 10000, 2);
setTimeout(() => burp('burp'), 0); // expect 2-7 'burp'
setTimeout(() => burp('burp'), 5000); // expect 5000 'burp'
setTimeout(() => burp('burp'), 6000); // expect 10000 'burp'
setTimeout(() => burp('burp'), 7000); // expect 15000 'burp'
The main problem is that for some reason, within the else block, the function isn't waiting for the setTimeout and is being called immediately. The syntax seems fine so I'm having a hard time figuring out why it's being called. This is the output after being called:
setTimeout(() => burp('burp'), 0); //6 'burp'
setTimeout(() => burp('burp'), 5000); //5001 'burp'
setTimeout(() => burp('burp'), 6000) //6001 'burp'
//allotedTime: 4005
setTimeout(() => burp('burp'), 7000); //10008 'burp'
//allotedTime: 4993
You'll notice that if you add the allotedTime with the result from the line above, you'll get the desired log. Thanks for taking a look.
Link to repl
Not sure if I understood the problem entirely, but based on your expectations,
the else interval should be:
interval - (Date.now() - records[totalCalls-maxCalls]))
because totalCalls is increment once the callback is called. So, either add totalCalls++; as the first statement of the else block (before setInterval()) or don't expect the value to be incremented (suggestion number 1).

Categories

Resources