For Loop SetTimeout Works Only 1 Time - javascript

I have a button with click function below. It has 3 nested for loop, and 1 setTimeout function.
The below loops are looping 5 times. I want below code to work(5x5) total 25 seconds of execution time, and each 5 seconds console output should be "Waited" printed.
However below code works only 5 seconds, and immediately prints "5 hello". Without changing my for loop structure, how can I make it work as I want?
jQuery("#btn_trendyolStocksSYNC").click(function() {
for(var product in all){
var colors = all[product];
for(var singleColor in colors[0]){
var size = colors[0][singleColor];
for(var index in size){
var singleSize = size[index];
setTimeout(function (){
console.log('Waited');
}, 5000);
}
}
}
});
Edit: I don't use the for loop with indexes, so solutions for number indexed for loops are not working for me.

You could try by adding await and a Promise:
jQuery("#btn_trendyolStocksSYNC").click(async function() {
for(var product in all){
var colors = all[product];
for(var singleColor in colors[0]){
var size = colors[0][singleColor];
for(var index in size){
var singleSize = size[index];
await new Promise(resolve => setTimeout(function (){
console.log('Waited');
resolve();
}, 5000));
}
}
}
});
What this does is simply tell your loop to stop and only continue once the Promise object calls its resolve parameter function. That way your delay should simply happen before the next iteration. This is the important code:
await new Promise(resolve => setTimeout(function (){
console.log('Waited');
resolve();
}, 5000));
It simply creates a Promise that we will resolve once the timeout has let 5000 milliseconds pass. Then we tell our loop to simply await that completion before continuing to the next item.
Note You also need to add async to your handler function, so javascript knows that this function can wait and take as long as it needs to.

The setTimeout(); function is asynchronous, meaning that your script will not wait for it to finish before moving on. That's why it has a callback.
Try something like this: (not the best method)
//delayed loop
var i = 1;
function loop() {
//wait 5 secs
setTimeout(function() {
console.log(i);
if(i>5) {
//cancel
return;
}
loop();
i++;
return;
}, 1000);
if(i>5) {
//cancel function
return;
}
}
//do the loop
loop();
Like what somethinghere said, you could put the setTimeout in the if statement.
Of course to do something after the loop ends you need a callback function.

you can use setInterval and clearInterval.
var n=0;
var a = setInterval(()=>{
console.log("Waited");
n++; if(n==5){clearInterval(a);}
},5000);

Related

Any easier way to create CSS value change animations with javascript [duplicate]

I need to execute 3 functions in a 1 sec delay.
for simplicity those functions are :
console.log('1');
console.log('2');
console.log('3');
I could do this: ( very ugly)
console.log('1')
setTimeout(function () {
setTimeout(function () {
console.log('2')
setTimeout(function () {
console.log('3')
}, 1000)
}, 1000)
}, 1000)
Or I could create an array of functions and use setInterval with global counter.
Is there any elegant way of doing this ?
(p.s. function no.2 is not dependent on function number 1... hence - every sec execute the next function.).
You can use something like this with setTimeout:
var funcs = [func1, func2, func3],
i = 0;
function callFuncs() {
funcs[i++]();
if (i < funcs.length) setTimeout(callFuncs, 1000);
}
setTimeout(callFuncs, 1000); //delay start 1 sec.
or start by just calling callFuncs() directly.
Update
An setInterval approach (be aware of the risk of call stacking):
var funcs = [func1, func2, func3],
i = 0,
timer = setInterval(callFuncs, 1000);
function callFuncs() {
funcs[i++]();
if (i === funcs.length) clearInterval(timer);
}
Assuming you run it on a modern browser or have added support for array.map this is quite concise:
[func1, func2, func3].map(function (fun, index) {
setTimeout(fun, 1000 + index * 1000);
}
setTimeout(function(){console.log('1')}, 1000);
setTimeout(function(){console.log('2')}, 2000);
setTimeout(function(){console.log('3')}, 3000);
There is a new type of function declaration called generators in es6 (a.k.a ecmascript 6, es2015). It is incredibly useful for this situation, and makes your async code look synchronous. es6 is the latest standard of JavaScript as of 2015. It works on modern browsers but you can use Babel and its javascript polyfill to use generators now even on older browsers.
Here is a tutorial on generators.
The function myDelayedMessages below is an example of a generator. Run is a helper function that takes a generator function which it calls and provides a function to advance the generator as the first argument of the generator function that it called.
function delay(time, callback) {
setTimeout(function () {
callback();
}, time);
}
function run(generatorFunction) {
var generatorItr = generatorFunction(resume);
function resume(callbackValue) {
generatorItr.next(callbackValue);
}
generatorItr.next()
}
run(function* myDelayedMessages(resume) {
for(var i = 1; i <= 3; ++i) {
yield delay(1000, resume);
console.log(i);
}
});
This is an overview of the code which is similar to the above tutorial's final overview.
run takes our generator and creates a resume function. run creates a
generator-iterator object (the thing you call next on), providing
resume.
Then it advances the generator-iterator one step to kick
everything off.
Our generator encounters the first yield statement
and calls delay.
Then the generator pauses.
delay completes 1000ms later and calls resume.
resume tells our generator to advance a single step.
Our generator continues from the spot it yielded at then console.logs i, which is 1, then continues the loop
Our generator encounters the second call to yield,
calls delay and pauses again.
delay waits 1000ms and ultimately
calls the resume callback. resume advances the generator again.
Our generator continues from the spot it yielded at then console.logs i, which is 2, then continues the loop.
delay waits 1000ms and ultimately
calls the resume callback. resume advances the generator again.
Our generator continues from the spot it yielded at then console.logs i, which is 3, then continues and the loop finishes.
There are no more calls to yield, the generator finishes executing.
with async/await
const pause = _ => new Promise(resolve => setTimeout(resolve, _));
async function main() {
await pause(1000);
console.log('one');
await pause(1000);
console.log('two');
await pause(1000);
console.log('three');
}
main();
note this works in a loop too
const pause = _ => new Promise(resolve => setTimeout(resolve, _));
async function main() {
for (let i = 0; i < 3; ++i) {
await pause(1000);
console.log(i + 1);
}
}
main();
I think most simple way to do this is to create some closures within a function.
First i'll recall that you have big interest in using setInterval, since the overhead of the setTimeout might have it trigger 10ms off target. So especially if using short (<50ms) interval, prefer setInterval.
So we need to store the function array, the index of latest executed function, and an interval reference to stop the calls.
function chainLaunch(funcArray, time_ms) {
if (!funcArray || !funcArray.length) return;
var fi = 0; // function index
var callFunction = function () {
funcArray[fi++]();
if (fi==funcArray.length)
clearInterval(chainInterval);
} ;
var chainInterval = setInterval( callFunction, time_ms);
}
Rq : You might want to copy the function array ( funcArray = funcArray.slice(0); )
Rq2 : You might want to loop within the array
Rq3 : you might want to accept additionnal arguments to chainlaunch. retrieve them with var funcArgs = arguments.slice(3); and use apply on the functions : funcArray[fi++].apply(this,funcArgs);
Anyway the following test works :
var f1 = function() { console.log('1'); };
var f2 = function() { console.log('2'); };
var f3 = function() { console.log('3'); };
var fArr = [f1, f2, f3];
chainLaunch(fArr, 1000);
as you can see in this fiddle : http://jsfiddle.net/F9UJv/1/
(open the console)
There are here two methods. One with setTimeout and anotherone with setInterval. The first one is better in my opinion.
for(var i = 1; i++; i<=3) {
setTimeout(function() {
console.log(i);
}, 1000*i);
}
// second choice:
var i = 0;
var nt = setInterval(function() {
if(i == 0) return i++;
console.log(i++);
if(i>=3) clearInterval(nt);
}, 1000);

Why is my setTimeout function not waiting for the time specified?

I have a for loop and I want to print its i'th value after a delay but with setTimeout() function, it waits for specified time but then every value of i prints without any delay. Is this because setTimeout() is an Async function and by the time it completes its first countdown, the time for all other values is also over.
for(let i=0;i<10;i++){
setTimeout(()=>{
console.log(i);
},10);
}
OUTPUT:
(10ms Gap) 1-2-3-4-5
OUTPUT REQUIRED: 1 - 10ms Gap - 2 -10ms Gap--... So on. Kindly provide the reason for solution.
You are repeatedly calling setTimeout() in your loop, if you just want to delay your loop, you could try something like this.
loopWithDelay();
async function loopWithDelay() {
for(let i = 0; i < 10; i++){
console.log(i)
await delay(100);
}
}
var timer;
function delay(ms) {
return new Promise((x) => {
timer = setTimeout(x, ms);
});
}
You are correct that the setTimeout is asynchronous, therefore every console.log(i) is set to run at basically the same time. I find it easier to use setInterval in your scenario:
let i = 0;
let myInterval = setInterval(() => {
console.log(i);
i++;
if (i === 10) {
clearInterval(myInterval);
}
}, 10);
You can modify the timer in the loop for every item.
for(let i=0;i<10;i++){
setTimeout(()=>{
console.log(i);
},10*(i+1));
}
This ensures proper gap between every item.
I hope it helps.
Yes, setTimeout() is an async function and all the countdowns start at almost the exact time because there is no waiting time between succesive setTimeout() calls.
What you can do in order to get the expected behaviour is put the for in a function and call that function back from setTimeout() when the countdown runs out:
function f(i) {
if(i < 10) {
setTimeout(()=> {
console.log(i);
f(i+1);
}, 10);
}
}
f(0);

Angular 4 setTimeout() with variable delay and wait

I have a list of events with timestamp.
What I want is to display the events based on the timestamp:
To add a delay:
delay = timestamp(t+1) - timstamp(t)
I know this doesn't work well with setTimeout, but there is an workaround, if the timeout is constant, in my case is not.
Is it possible to make the next setTimeout() wait for the previous one? To be specific, if the first setTimeout() has a 5 second delay and the second one has 3 seconds, the second one will appear first. I want them to be in the same order but execute one after the other.
This example works for a constant delay, but I want to calculate the delay based on the information I take iterating the list.
for (i = 1; i <= 5; ++i) {
setDelay(i);
}
function setDelay(i) {
setTimeout(function(){
console.log(i);
}, 1000);
}
You can use IIFE (Immediately Invoked Function Expression) and function recursion instead. Like this:
let i = 0;
(function repeat(){
if (++i > 5) return;
setTimeout(function(){
console.log("Iteration: " + i);
repeat();
}, 5000);
})();
Live fiddle here.
When using the latest Typescript or ES code, we can use aync/await for this.
let timestampDiffs = [3, 2, 4];
(async () => {
for (let item of timestampDiffs) {
await timeout(item * 1000);
console.log('waited: ' + item);
}
})();
function timeout(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
We need to wrap the for loop in an async immediately invoked function because to support await.
We also do need a timeout function that returns a promise once the timeout is complete.
After that, we wait for the timeout to complete before continuing with the loop.
Don't call it within a loop, as it won't wait for the setTimeout to complete.
You can instead pass a list of times that you want to wait, and iterate them recursively:
let waits = [5000, 3000, 1000];
function setDelay(times) {
if (times.length > 0) {
// Remove the first time from the array
let wait = times.shift();
console.log("Waiting", wait);
// Wait for the given amount of time
setTimeout(() => {
console.log("Waited ", wait);
// Call the setDelay function again with the remaining times
setDelay(times);
}, wait);
}
}
setDelay(waits);
You can set time out period using i value dynamically. Like
for (i = 1; i <= 5; ++i) {
setDelay(i);
}
function setDelay(i) {
setTimeout(function(){
console.log(i);
}, i*1000);
}
You can think of something like that:
setDelay(1,5);
function setDelay(i, max) {
setTimeout(function(){
console.log(i);
if(i < max){
i++;
setDelay(i, max);
}
}, 1000);
}
The idea is to set the next setTimeout(.. in a timeout.
Of course you can modify it to have different long timeouts as well.
Hope i can help :)

How to set time delay in javascript

I have this a piece of js in my website to switch images but need a delay when you click the image a second time. The delay should be 1000ms. So you would click the img.jpg then the img_onclick.jpg would appear. You would then click the img_onclick.jpg image there should then be a delay of 1000ms before the img.jpg is shown again.
Here is the code:
jQuery(document).ready(function($) {
$(".toggle-container").hide();
$(".trigger").toggle(function () {
$(this).addClass("active");
$(".trigger").find('img').prop('src', 'http://localhost:8888/images/img_onclick.jpg');
}, function () {
$(this).removeClass("active");
$(".trigger").find('img').prop('src', 'http://localhost:8888/images/img.jpg');
});
$(".trigger").click(function () {
$(this).next(".toggle-container").slideToggle();
});
});
Use setTimeout():
var delayInMilliseconds = 1000; //1 second
setTimeout(function() {
//your code to be executed after 1 second
}, delayInMilliseconds);
If you want to do it without setTimeout: Refer to this question.
setTimeout(function(){
}, 500);
Place your code inside of the { }
500 = 0.5 seconds
2200 = 2.2 seconds
etc.
ES-6 Solution
Below is a sample code which uses aync/await to have an actual delay.
There are many constraints and this may not be useful, but just posting here for fun..
const delay = (delayInms) => {
return new Promise(resolve => setTimeout(resolve, delayInms));
}
const sample = async () => {
console.log('a');
console.log('waiting...')
let delayres = await delay(3000);
console.log('b');
}
sample();
you can use the promise
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
then use this method
console.log("Hello");
sleep(2000).then(() => { console.log("World!"); });
or
console.log("Hello");
await sleep(2000);
console.log("World!");
There are two (mostly used) types of timer function in javascript setTimeout and setInterval (other)
Both these methods have same signature. They take a call back function and delay time as parameter.
setTimeout executes only once after the delay whereas setInterval keeps on calling the callback function after every delay milisecs.
both these methods returns an integer identifier that can be used to clear them before the timer expires.
clearTimeout and clearInterval both these methods take an integer identifier returned from above functions setTimeout and setInterval
Example:
setTimeout
alert("before setTimeout");
setTimeout(function(){
alert("I am setTimeout");
},1000); //delay is in milliseconds
alert("after setTimeout");
If you run the the above code you will see that it alerts before setTimeout and then after setTimeout finally it alerts I am setTimeout after 1sec (1000ms)
What you can notice from the example is that the setTimeout(...) is asynchronous which means it doesn't wait for the timer to get elapsed before going to next statement i.e alert("after setTimeout");
Example:
setInterval
alert("before setInterval"); //called first
var tid = setInterval(function(){
//called 5 times each time after one second
//before getting cleared by below timeout.
alert("I am setInterval");
},1000); //delay is in milliseconds
alert("after setInterval"); //called second
setTimeout(function(){
clearInterval(tid); //clear above interval after 5 seconds
},5000);
If you run the the above code you will see that it alerts before setInterval and then after setInterval finally it alerts I am setInterval 5 times after 1sec (1000ms) because the setTimeout clear the timer after 5 seconds or else every 1 second you will get alert I am setInterval Infinitely.
How browser internally does that?
I will explain in brief.
To understand that you have to know about event queue in javascript. There is a event queue implemented in browser. Whenever an event get triggered in js, all of these events (like click etc.. ) are added to this queue. When your browser has nothing to execute it takes an event from queue and executes them one by one.
Now, when you call setTimeout or setInterval your callback get registered to an timer in browser and it gets added to the event queue after the given time expires and eventually javascript takes the event from the queue and executes it.
This happens so, because javascript engine are single threaded and they can execute only one thing at a time. So, they cannot execute other javascript and keep track of your timer. That is why these timers are registered with browser (browser are not single threaded) and it can keep track of timer and add an event in the queue after the timer expires.
same happens for setInterval only in this case the event is added to the queue again and again after the specified interval until it gets cleared or browser page refreshed.
Note
The delay parameter you pass to these functions is the minimum delay
time to execute the callback. This is because after the timer expires
the browser adds the event to the queue to be executed by the
javascript engine but the execution of the callback depends upon your
events position in the queue and as the engine is single threaded it
will execute all the events in the queue one by one.
Hence, your callback may sometime take more than the specified delay time to be called specially when your other code blocks the thread and not giving it time to process what's there in the queue.
And as I mentioned javascript is single thread. So, if you block the thread for long.
Like this code
while(true) { //infinite loop
}
Your user may get a message saying page not responding.
For sync calls you can use the method below:
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++) {
if ((new Date().getTime() - start) > milliseconds){
break;
}
}
}
If you need refresh, this is another posibility:
setTimeout(function () {
$("#jsSegurosProductos").jsGrid("refresh");
}, 1000);
I'll give my input because it helps me understand what im doing.
To make an auto scrolling slide show that has a 3 second wait I did the following:
var isPlaying = true;
function autoPlay(playing){
var delayTime = 3000;
var timeIncrement = 3000;
if(playing){
for(var i=0; i<6; i++){//I have 6 images
setTimeout(nextImage, delayTime);
delayTime += timeIncrement;
}
isPlaying = false;
}else{
alert("auto play off");
}
}
autoPlay(isPlaying);
Remember that when executing setTimeout() like this; it will execute all time out functions as if they where executed at the same time assuming that in setTimeout(nextImage, delayTime);delay time is a static 3000 milliseconds.
What I did to account for this was add an extra 3000 milli/s after each for loop incrementation via delayTime += timeIncrement;.
For those who care here is what my nextImage() looks like:
function nextImage(){
if(currentImg === 1){//change to img 2
for(var i=0; i<6; i++){
images[i].style.zIndex = "0";
}
images[1].style.zIndex = "1";
imgNumber.innerHTML = imageNumber_Text[1];
imgDescription.innerHTML = imgDescText[1];
currentImg = 2;
}
else if(currentImg === 2){//change to img 3
for(var i=0; i<6; i++){
images[i].style.zIndex = "0";
}
images[2].style.zIndex = "1";
imgNumber.innerHTML = imageNumber_Text[2];
imgDescription.innerHTML = imgDescText[2];
currentImg = 3;
}
else if(currentImg === 3){//change to img 4
for(var i=0; i<6; i++){
images[i].style.zIndex = "0";
}
images[3].style.zIndex = "1";
imgNumber.innerHTML = imageNumber_Text[3];
imgDescription.innerHTML = imgDescText[3];
currentImg = 4;
}
else if(currentImg === 4){//change to img 5
for(var i=0; i<6; i++){
images[i].style.zIndex = "0";
}
images[4].style.zIndex = "1";
imgNumber.innerHTML = imageNumber_Text[4];
imgDescription.innerHTML = imgDescText[4];
currentImg = 5;
}
else if(currentImg === 5){//change to img 6
for(var i=0; i<6; i++){
images[i].style.zIndex = "0";
}
images[5].style.zIndex = "1";
imgNumber.innerHTML = imageNumber_Text[5];
imgDescription.innerHTML = imgDescText[5];
currentImg = 6;
}
else if(currentImg === 6){//change to img 1
for(var i=0; i<6; i++){
images[i].style.zIndex = "0";
}
images[0].style.zIndex = "1";
imgNumber.innerHTML = imageNumber_Text[0];
imgDescription.innerHTML = imgDescText[0];
currentImg = 1;
}
}
I'm not an expert in JS domain but I've found a workaround for this problem using setTimeout() and a recursive function as follows:
i=0; //you should set i as a global variable
function recFunc() {
i++;
if (i == 1) {
//do job1
} else if (i == 2) {
//do job2
} else if (i == 3) {
//do job3
}
if (i < 3) { //we have 3 distinct jobs. so the condition is (j < 3)
setTimeout(function () {
recFunc();
}, 2000); //replace 2000 with desired delay
}
}
//
//
//
recfunc(); //start the process
const delay = (delayInms) => new Promise(resolve => setTimeout(resolve, delayInms));
await delay(100)

jQuery Promise with setTimeout

I need one setInterval to start after another setInterval ends. Is there a way to do this with promises?
Ideally, I would like the code to look something like this:
for (i=0; i<5; i++){
setInterval(fun_1, 1000)
//wait until this is done
}
The setInterval calls the provided function after given interval of time until you clear its object. So you do not need a loop for this. You can use a counter to terminate it after counter reaches desired value.
Live Demo.
var myInter;
i = 1;
function fun_1()
{
// some code
if(i++==5)
clearInterval(myInter);
}
myInter = setInterval(fun_1, 1000);
Do you mean that you would like the second interval to begin the countdown after the previous interval is done? In that case, you would say
window.setTimeout(fun_1, 1000);
function fun_1
{
window.setTimeout(fun_2, 1000);
}
This starts the countdown for the second timeout after the first one has completed.
You should call the next interval after the first one completes
var cnt = 0;
function executeNextStep(){
fun_1();
if (cnt<5) {
window.setTimeout(executeNextStep,1000);
}
cnt++;
}
executeNextStep(); //execute this right away
//window.setTimeout(executeNextStep,1000); use this if you want it to be delayed
if you need to execute different functions:
function fun_1() {
console.log(1);
}
function fun_2() {
console.log(2);
}
function fun_3() {
console.log(3);
}
var fnc_stack = [fun_1, fun_2, fun_3];
var cnt = 0;
function executeNextStep(){
fnc_stack[cnt]();
cnt++;
if (cnt<fnc_stack.length) {
window.setTimeout(executeNextStep,1000);
}
}
executeNextStep();

Categories