the function should change the background to red and to blue with 1 second
between the two operations, but when i run it i find that the first change does not appear on the page
i put the "sleep" function as i thought the 2 changes happen in the same time
function changeBackGround()
{
document.body.style.backgroundColor = "red";
sleep(1000);
document.body.style.backgroundColor = "blue";
}
function sleep(milliseconds) {
var start = new Date().getTime();
for (var i = 0; i < 1e7; i++)
{
if ((new Date().getTime() - start) > milliseconds)
{
break;
}
}
}
i expect that the page background should first be red and after 1 second it becomes blue
Your homebrewed sleep function locks up the event loop in a busy loop. This prevents the browser from repainting the page, so while the DOM has updated, the background isn't reflected in the rendered page.
Use setTimeout if you want to run a bit of code after some time.
you can define the function to accept the color as an argument like this:
function changeBackGround(color) {
document.body.style.backgroundColor = color;
}
and you can use setTimeout to execute a function after given period in milliseconds
window.onload = function() {
changeBackGround('red');
setTimeout(function() {
changeBackGround('blue');
}, 1000);
}
Related
I am making a memory game in javascript that I am putting on my web page. I have 3 buttons labeled 1, 2, and 3, and they are supposed to turn green then white again in a certain order. The order is random and increases by 1 every turn. The player is then supposed to click the buttons in the correct order. The problem is that when I change the button color to green, instead of each button turning green and then white again in the correct order, the buttons turn green all at once. This is the javascript logic:
function lightUp() {
var arrayOrder = gameOrder.split("");
for(let i = 0; i < arrayOrder.length; i++) {
document.getElementById("but" + arrayOrder[i]).style.backgroundColor = "green";
setTimeout(function() {
document.getElementById("but" + arrayOrder[i]).style.backgroundColor = "white";
}, 1000);
}
}
gameOrder is a string of the number order (e.g. "12331232123")
I think the game isn't working because of the way setTimeout works, I believe it pauses in the background and allows the for loop to keep running instead of pausing the whole function for a second (which is what I am trying to do).
I want each button to turn on then off before the next button changes color. So if the order is 1 2 3, I want button 1 to turn green and then white then button 2 to turn green then white and finally for button 3 to turn green then white.
As espacarello has pointed out, code you run with setTimeout is not synchronous. You could make the function asynchronous and await the setTimeout.
This is my preferred way to "animate" things with JS:
async function lightUp() {
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
for (const i of gameOrder.split('')) {
const el = document.getElementById(`but${i}`);
el.style.backgroundColor = 'green';
await sleep(1000);
el.style.backgroundColor = 'white';
}
}
This stays simple and elegant without messying your code with numerous callbacks, recursion, or nested setTimeouts. It stays as one function.
Edit: It is important to note that IE does not support Promises or the Arrow Function syntax. See the other two answers for wider support.
Problem with your code is the for loop runs all at once without a delay. You code it thinking that the interval will cause the loop to wait until it is executed. Problem with that is, will just run without waiting.
Use a queue type of system where you run the step and when it is done, you run the next until you run out of things to do.
var steps = [1,2,3,2,1,3,1]
var delay = 1000
var step = 0
function next () {
var button = document.getElementById('btn' + steps[step])
button.classList.add("on")
window.setTimeout( function () {
button.classList.remove("on")
step++
if(step<steps.length) next()
}, delay)
}
next()
.on {
background-color: green;
}
<button id="btn1">One</button>
<button id="btn2">Two</button>
<button id="btn3">Three</button>
you can do it as below...
function lightUp() {
var arrayOrder = gameOrder.split("");
for (let i = 0; i < arrayOrder.length; i++) {
(function(i) {
setTimeout(function() {
document.getElementById("but" + arrayOrder[i]).style.backgroundColor = "green";
}, 1000 * i - 1000);
setTimeout(function() {
document.getElementById("but" + arrayOrder[i]).style.backgroundColor = "white";
}, 1000 * i); // schedules excution increasingly for each iteration
})(i);
}
}
to learn more about how it works please refere to this answer
I have 5 divs that I want to change the color of when the function is called, however each time the function is called the time given in the set interval seems to decrease.
let runways = [runway1, runway2, runway3, runway4, runway5];
const checkRunway = () =>{
for(let i = 0; i < runways.length; i++){
if(runways[i].available === true){
runways[i].available = false
document.getElementById(`runway${[i]}`).style.backgroundColor = 'red'
break;
}
}
}
const landing = () =>{
if(selection.value == 'DC35'){
checkRunway();
setInterval(function(){
runways[i].available = true;
document.getElementById(`runway${[i]}`).style.backgroundColor = 'green'
}, 1000)
}
}
Your setInterval time isn't decreasing, it isn't stopping. You've never told it to stop, so what's most likely happening is that you're duplicating the setIntervals each time you call the function, meaning that multiples are running at once time. This would give you the false impression that your current click, causes a reaction, sooner. When in fact, it was a previous setInterval, executing.
If you only want it to happen once, say onclick, then use setTimeout instead.
setTimeout(function() {
runways[i].available = true;
document.getElementById(`runway${[i]}`).style.backgroundColor = 'green'
}, 1000);
I did some research about loops and setTimeout function, but still its not working as I wish...
It opens all the links in same time without delay of 5 seconds per link that opened.
I would like it to open every link with 5 second delay after every each of them.
Code:
var links = document.querySelectorAll('a[class="mn-person-info__link ember-view"][id^="ember"]')
for (var i = 1; i <= links.length; i++) {
(function(index) {
setTimeout(function() {
window.open(links[index].href,'_blank');
}, 5000);
})(i);
}
Using a Promise chain and Array#reduce(), you can do this:
var links = document.querySelectorAll('a[class="mn-person-info__link ember-view"][id^="ember"]');
Array.from(links).reduce((chain, { href }) => {
return chain.then(() => new Promise(resolve => {
window.open(href, '_blank');
setTimeout(resolve, 5000);
}));
}, Promise.resolve())
If you don't want to do something quite this fancy and you're fine setting all your timeouts at once, you can simplify your for loop using let instead of var and an IIFE:
var links = document.querySelectorAll('a[class="mn-person-info__link ember-view"][id^="ember"]');
for (let i = 0; i < links.length; i++) {
setTimeout(function() {
window.open(links[i].href, '_blank');
}, 5000 * i);
}
Or even simpler, using for...of and object destructuring:
var links = document.querySelectorAll('a[class="mn-person-info__link ember-view"][id^="ember"]');
var i = 0;
for (const { href } of links) {
setTimeout(function() {
window.open(href, '_blank');
}, 5000 * i++);
}
That's because all your timeouts are set immediately, almost at one time. So the ends of timeouts are take place almost on the same time. Try this:
var links = document.querySelectorAll('a[class="mn-person-info__link ember-view"][id^="ember"]')
for (var i = 1; i <= links.length; i++) {
(function(index) {
setTimeout(function() {
window .open(links[index].href,'_blank');
}, 5000 * i);
})(i);
}
setTimeout is asyncronous. I would solve this specified problem with:
Array.from(links).reduce((a,e)=>{
setTimeout(function() { window .open(e.href,'_blank');}, a);
return a + 5000;
} ,5000);
From the code above i assume that you wish to open the links once every 5 seconds so here are the improvements and suggestion made upon your code
// use JS hoisting for variable declarations
var
// Timer to store the next timer function reference
timer,
// The delay between each function call
delay = 5000,
// Set the desired selector
selectors = 'a[class="mn-person-info__link ember-view"][id^="ember"]',
// Get the list of the required selectors.
links = document.querySelectorAll(selectors);
// Create a function which will be called every X seconds
function openLink( index ){
// validate that the index is not out of bound
if (index === links.length){
return;
}
// get the current link and open new window with the link url
window.open(links[index].href,'_blank');
// Set the next timer to open the next window
timer = setTimeout( openLink, delay, ++index);
}
// Call the function for the first time with index = 0
openLink( 0 );
What does this code do?
The first section is declaration of the variables which will be used in this script. The preferred way to declare variables id to to use hoisting
Hoisting
Hoisting is a JavaScript mechanism where variables and function declarations are moved to the top of their scope before code execution.
Timers
If you wish to open the links in a sequence you should put them inside a function which will call them one after the other instead of using the for loop. The for loop will place all of them in the call stack/event loop and all of them will be executed after 5000 milliseconds since this is what set time out will so, it will schedule the execution of the code to 5000 milliseconds from now for all of them.
Im recommending you to watch the amazing lecture by Philip Roberts
Philip Roberts: What the heck is the event loop anyway
Saving the return value from the setTimeout will allow you to later on cancel the timer if you wish using the clearTimeout( timer )
Summary
Since you had a for loop it will simply loop over all your links. The setTimeout will set the scheduled execution time 5 seconds from now for all the links.
The trick here is to set the next timer after the current one is opened. This is why the sertTimeout is defined within the function itself.
The setTimeout gets a third param which is the parameter passed to the function when its being called.
Easy way:
links.forEach(function(i, link) {
setTimeout(function() {
window.open(link.href,'_blank');
}, 5000 * i);
});
Just wait i * 5 seconds, where i is the index of the link
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)
There is a function which sets an interval using setInterval(), but even after calling clearInterval(), I can see in the console that the else condition is still running. How can I clear that interval properly?
function increase(old, step, neu) {
var i = 0;
var delay2;
function countUp() {
if (i < 5) {
old += step;
// console.log("increase")
$("#total-price-value").text(old + " dollors");
$("#total-price-value").digits();
i++;
delay2 = setInterval(countUp, 80);
} else {
clearInterval(delay2);
console.log(delay2);
}
}
countUp();
}
It looks like you're a little confused about the difference between timeouts and intervals. Timeouts fire only once; intervals fire many times. If you're using an interval, you probably only want to set it once (you're setting it every time). If you're using a timeout, you probably want to set it every time (like you're doing).
In order to fix the problem, you'll either want to switch to timeouts (probably the easiest; just a search/replace) or only set the interval once.
For example, here is how one might use setTimeout to count up to five:
var count = 0;
function timeoutFired() {
count++;
if(count < 5) {
setTimeout(timeoutFired, 1000);
}
}
setTimeout(timeoutFired, 1000);
Using timeouts, we don't need to clear to stop it from counting; simply not setting a timeout will prevent it from running again.
Here is how one might use setInterval:
var count = 0;
function intervalFired() {
count++;
if(count >= 5) {
clearInterval(interval);
}
}
var interval = setInterval(intervalFired, 1000);
If you want some code running periodically using intervals to stop, you must call clearInterval. Note that we only call setInterval once, versus setTimeout every time we didn't want it to continue.
Apparently, you have mistaken setInterval for setTimeout. setInterval runs the enclosed function every n milliseconds while setTimeout executes only once after n milliseconds.
I suppose you wanted to "tick until 5" so here's a sample:
function increase(old, step, neu) {
var i = 0;
interval = setInterval(function() {
if (i < 5) {
//do something at this "tick"
console.log(i);
i++;
} else {
//else, stop
clearInterval(interval);
}
},80);
}
increase();