The code below allows me to have an array with a set of numbers such as "thearray=[2,8,9]" and loop through that array, and for each number item in the array for example "2,8,9", the code calls a function an amount of times equal to the current number item in the array. So if the current number item is 2, the function gets called twice.
After the set of calls, there is a pause, and then the function is called again an amount of times equal to the current number in the array etc. In other words, as the array is being looped through, if the current number item is 2, the function named "thefunction" will be called twice then there is a pause and the function "thefunction" is then again called an amount of times equal to the next number in the array.
In my case, "thefunction" simply displays an alert box message two times followed by a pause, then 8 times followed by a pause, then 9 times followed by a pause etc. Of course, with the alert box messages, I get the messages in sequence because I must select ok before I see the next alert message. The problem is, I can't get the calls to "thefunction" to appear sequential like when the code to be executed within "thefunction" displays an alert box, when other code such as appending an li item with data to a ul is within that function.
It's as if the 2 calls are made at once, then the 8 calls are made at once, etc. Even though that may not be the case, it happens so fast, it seems like it. I would like to slow it down. So if the code within "thefunction" was code that would append information to an li element, instead of just seeing the information rapidly added where the sequence of calls isn't noticeable, I would like there to be a delay so that when li elements are appended, the sequence is more obvious rather than rapid where it's hard to see the sequence.
Here is the code:
function runArray(arr, fn) {
// initialize array index - can't use for loop here with async
var index = 0;
function next() {
var cnt = +arr[index];
for (var i = 0; i < cnt; i++) {
fn(index, cnt);
}
// increment array index and see if there's more to do
++index;
if (index < arr.length) {
setTimeout(next, 400);
}
}
// start the whole process if the array isn't empty
if (arr.length) {
next();
}
}
runArray(thearray, shakeit);
and here is a jsfiddle demonstrating a log rapidly adding information. I want to slow it down so the information is added slow enough to make it obvious there is a sequence.
http://jsfiddle.net/jfriend00/Loycmb3b/
What you want to do in essence is insert a delay between executions of the for loop. The only sane way to introduce a delay in JavaScript is using setTimeout and setInterval, so that's what you have to work with.
The next thought is that since each loop iteration is to be implemented as a callback to setTimeout and friends, the logic that moves to the next array element after each loop completes is necessarily going to be part of that -- you can't move to the next element before the loop completes.
But the logic that moves to the next element is already inside next, and we already established that next is supposed to set up the callback. So next is going to schedule the callback, and the callback is also going to schedule next -- there's no other way.
Therefore:
function runArray(arr, fn, delay) {
var index = 0;
var cnt = 0;
var i = 0;
// Called once for each array element
function next() {
if (index >= arr.length) {
return;
}
cnt = +arr[index];
i = 0;
loop();
}
// Represents a single iteration of what was previously a for loop
// Will either schedule the next iteration or move to the next element
function loop() {
if (i < cnt) {
fn(index, i++);
setTimeout(loop, delay); // delay before next iteration
}
else {
++index;
setTimeout(next, delay); // delay before moving to next element
}
}
if (arr.length) {
next();
}
}
I kept the same delay both between "loop iterations" and between the end of a loop and the start of the next one, but that can easily be changed.
See it in action.
Unless I'm misunderstanding something...
function runArray(arr, fn, delay, idx, cnt) {
idx = idx || 0;
cnt = cnt || 0;
if(cnt >= arr[idx]) {
idx++;
cnt = 0;
}
if(idx >= arr.length)
return;
fn(idx, cnt);
setTimeout(function() { runArray(arr, fn, delay, idx, cnt + 1) }, delay);
}
http://jsfiddle.net/Loycmb3b/8/
I may or may not understand what you are trying to do, but with this piece of code here
runArray(theArray, theFunction, 400);
you are executing theArray and theFunction to occur after 400 ms, 1000 is one second, so if you want it to be a more substantial pause, increase that 400, here I increased it to 4000(4 seconds) and the pause is much more noticable.
http://jsfiddle.net/Loycmb3b/7/
Related
I have a class test whose background-color I want to flip between lime and green faster and faster.
For that, I'm using a for loop variable and passing it to a function containing a setTimeout(), but it's not working.
(This is not a duplicate question. The said "original" is about a simple setTimeout() whereas this question is about a setTimeout() within a for loop. I understand that the answers on that question might indirectly answer mine, but the questions themselves aren't the same)
$(document).ready(function() {
for (var i = 0; i < 20; i++) {
delay(i);
$(".test").css('background-color', 'lime');
}
});
function delay(i) {
setTimeout(function() {
$(".test").css('background-color', 'green');
}, 1000 - 50 * i);
}
.test {
width: 300px;
height: 300px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test"></div>
Try this way:
for(var i=0;i<20;i++)
{
delay(i);
}
function delay(i) {
setTimeout(function() {
if (i%2 == 0) {
$(".test").css('background-color', 'green');
} else {
$(".test").css('background-color', 'lime');
}
}, 1000 - 50 * i);
}
The problem is the loop executes faster than the timeout. setTimeout function basically says execute the given function after a certain time. The for loop you created there will continue without waiting for the code inside the setTimeout function to be executed, In other words your code producing 20 functions that will be executed in the future.
There are many way to produce the functionality you need.
To keep it simple and solve it you should create two functions instead:
$(document).ready(function() {
for (var i = 0; i < 20; i++) {
delay_lime(i);
delay_green(i+1);
}
});
function delay_green(i) {
setTimeout(function() {
$(".test").css('background-color', 'green');
}, 1000 - 50 * i);
}
function delay_lime(i) {
setTimeout(function() {
$(".test").css('background-color', 'lime');
}, 1000 - 50 * i);
}
try this: here is a example example
$(document).ready(function() {
delay();
var start = 0;
delay(start);
function delay(start) {
setTimeout(function() {
if(start == 0 ){
$(".test").css('background-color', 'green');
start = 1;
}else{
$(".test").css('background-color', 'red');
start = 0;
}
delay(start);
}, 100);
}
});
If you want to use a for loop, you should turn its containing function into an async function and await promises that resolve at the desired time:
const delay = (i) => new Promise(resolve => {
setTimeout(resolve, 1000 - 50 * i);
});
function changeToGreen() {
$(".test").css('background-color', 'green');
}
function changeToLime() {
$(".test").css('background-color', 'lime');
}
(async () => {
for (var i = 0; i < 20; i++) {
await delay(i);
changeToLime();
await delay(i);
changeToGreen();
}
})();
Your loop doesn't wait for any of the timeouts to occur, it runs through and queues up the events which will fire at the relevant intervals.
However, whilst doing so it sets the background color to lime a number of times.
After the loop has finished, the queued intervals start firing, and they set the background color to green a number of times.
But the colours do not alternate as the code execution is not in the order you expect.
Also, the multiple calls to setInterval queue the events to be fired after the specified delay. The code does not wait for the allotted time and then fire the next one. So your could of 1000 - 50 * i actually queues the latest event first, and so on until it queues the event that will actually fire first. Does that make sense? It will be more intuitive for you to set these in the order that they will fire. You could achieve the reducing delay by incrementing the timeout by a variable which reduces, e.g.
time = 1000;
delay = 1000;
setTimeout (blah, time);
time += delay;
delay -= 50;
setTimeout (blah, time);
// etc.
You could achieve an alternating effect by setting alternate intervals to be green and lime. For that a simple toggle variable would help.
color = 1;
color = 1 - color; // toggles between 0 and 1
useColor = ["lime", "green"][color];
I shan't rewrite your entire program for you, but I can assist more if you have specific questions. The best way to learn is to do.
There is a slight misunderstanding about the way timeouts work in the example code. Timeouts are asynchronous, meaning that they execute out of the normal order of execution. As a result, the lime green is shown immediately, and then at various times later the background is repeatedly changed to green; although, the only time the change is noticed is the first time as changing from green to green has no effect.
setTimeout creates a task, JavaScript in a browser is single threaded and will execute tasks through a task scheduler.
Using 1000 - 50 * i from 0 to 19 in the approach shown in the question will result in timeouts being scheduled for execution. First at 1000, then at 950, etc. However, they are all scheduled at the exact same time. So there is no difference scheduling them in forward or reverse order as the only relevant metric used is the time. Essentially the result is that every 50 milliseconds, the background color is set to green in this example.
Unfortunately, tasks that get executed in the browser are not executed exactly on time, and using this will aim at 50 milliseconds per call, but due to Operating System scheduling and depending on the system in use the result could be wildly different.
This could have been done with an interval just as easily, where the interval used was 50 milliseconds (although it would still suffer from the aforementioned OS issue). That said, there is no acceleration being used there. A better approach here, since we are dealing with animation (the colors flashing) would be to instead use requestAnimationFrame MDN.
requestAnimationFrame will attempt to run your code at 60 frames per second, or roughly 16.6 milliseconds per frame (1000 milliseconds / 60 frames).
Given that the goal was acceleration, a rate could be put in place to ramp the flashing.
// Cache the constructed jQuery object for element with class "test"
var testCache = $('.test');
// Create a set of colors to use in the flashing
var colors = ['lime','green'];
// Use a variable for a switch between the two colors
var colorSwitch = 0;
// Keep track of how many times the color has flashed
var i = 0;
// Used for tracking the start of an animation sequence
var start;
// In order to facilitate acceleration, use a function for
// determining the time between flashes,
// used an offset x^2 line at (20,16) with a 2x width
// y = 1/2(x-19)^2 - 19x + 16
var ft = t => 0.5*(t-19)*(t-19) - (t-19) + 16;
// This function will be called every 16.6 milliseconds
// by requestAnimationFrame, the timestamp is automatically injected
(function flashAccel(timestamp){
// Loop control to ensure only 20 flashes occur
if(i >= 20) return;
// Track the start of the timing for the animation sequence
start = start || timestamp;
// This is the milliseconds since the last sequence was updated
var elapsed = timestamp - start;
// Check to see if enough time has elapsed based on the acceleration
// function's value and the current value, if it has then update the view
if( elapsed > ft(i) ){
// Swaps between 0 and 1
colorSwitch = 1 - colorSwitch;
// Selects 0 or 1 indexed color
var color = colors[colorSwitch];
testCache.css('background-color',color);
// Update metrics
i++;
start = timestamp;
}
// Request the function to be called again in roughly 16.6 milliseconds
window.requestAnimationFrame(flashAccel);
})()
.test {
width: 300px;
height: 300px
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="test"></div>
function append(what) {
$("#drawer").append(what);
}
function outerHtml(o) {
return $("<div />").append($(o).clone()).html();
}
var allPixel = [];
$(".pix").each(function() {
allPixel.push(outerHtml($(this)));
});
$("#drawer").empty();
var index;
for (index = 0; index < allPixel.length; index++) {
pixel = allPixel[index];
setTimeout(append(pixel), 100);
}
I have a Container (#drawer) that is full of many div elements. In this function the HTML of each of these div elements gets saved in an array individually. When its done doing that the div element gets cleared.
In the Array allPixel are now all div elements. I want it so that each div element gets added to #drawer with a delay of 100ms.
Problem:
If I run this function nothing happens (the divs are not disappearing/ appearing). What did I do wrong?
If anything is unclear feel free to let me know and I will edit my question.
Question aswered... See it in action: https://wiese2.lima-city.de/test/
You are invoking the method invoking immediately and passing its return value i.e. undefined to setTimeout.
You can set additional parameters to be passed to append function when timer expires to setTimeout method.
Use
setTimeout(append, 100 * (i + 1), pixel);
Additionally you also need to increase timer based on element index
Implement a function that takes a function as its first argument, a number num as its second argument, then executes the passed in function num times.
function repeat(operation, num) {
var num_array = new Array(num);
for(var i = 0; i < num_array.length; i++){
return operation(num);
}
}
//
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat
RESULTS:
ACTUAL EXPECTED
------ --------
"Called function 1 times." "Called function 1 times."
"" != "Called function 2 times."
null != ""
# FAIL
Why doesn't this work?
I am assuming that I am starting a function called repeat. Repeat has two parameters and takes two arguments.
For the loop I create an array which has a length which is equal to the num passed in.
I then start a for loop, setting a counter variable i to 0. Then I set a conditional which states that i should always be less than the length of the num_array which was created earlier. Then the counter i is incremented up by one using the ++.
For every time that the conditional is true, we should return the value of calling running the function operation and passing the num as an argument.
The last two lines allow for easy running of the program through command line with pre programmed arguments being used.
Thank you for your time!
The return statement is breaking out of the function on the first iteration of the loop. You need to remove the return, and just call the function like this:
function repeat(operation, num) {
for(var i = 0; i < num; i++){
operation(num);
}
}
Note that I have removed the creation and iteration of the array, you do not need it for what you are doing here.
Also your initial question does not specify that you need to pass num to the function (but you do list it in your steps below), so you may be able to just do operation() instead of operation(num).
You probably want something like the below, rather than returning the result of the function operation(num) you want to store the value in teh array. return in a loop breaks out of the loop, so it would always only run once..
function repeat(operation, num) {
var num_array = new Array(num);
for(var i = 0; i < num_array.length; i++){
num_array[i] = operation(num);
}
}
//
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat
If you are asking why the loop is not running, it's because you have to run the function after defining it (I'm assuming you are not already calling the function somewhere else).
Once calling the function repeat you will see that it is exiting after one iteration. This is because you are returning the operation - returning causes the function to end. To stay in the loop you just need to call operation(), without the return.
Also you don't need to create an array, you can just use the counter you are defining in the for loop.
So the code would look something like this:
var op = function(arg) {console.log(arg);},
n = 5;
function repeat(operation, num) {
for(var i = 0; i < num; i++){
operation(i);
}
}
repeat(op ,n);
// The next lines are from a CLI, I did not make it.
//
// Do not remove the line below
module.exports = repeat
I would like to call a function a specific amount of times. I would like the number of times I would like my function to be called in an array as seen below. I have a for loop that goes through the array and I am attempting to call the function named "thefunction"
as many times as the number in the array item. So the first item in the array is 8, so I first want "thefunction" function to be called 8 times which means I should see the alert message "thefunction" will display, 8 times and then 2 times as 2 is the next item in the array then 15 times. I'd also like to pause for a moment in between each set of calls. So after the function is called 8 times, it will pause for a moment before calling the function 2 times then again before it calls the function 15 times and on and on. Here is my code so far.
var thearray = ['8','2','15'];
for(j=0; j < thearray.length; j++){
num = thearray[j];
var counter = 1;
(function foo() {
thefunction();// function I'm calling
if (counter < num) {
counter++;
setTimeout(foo, 400);
}
})();
}
function thefunction (){
alert('test');
}
You can't use the for loop with the variable j and use an asynchronous setTimeout(). The for loop will run to completion and the j variables will have already increased before the setTimeout() runs. This is a classic problem with trying to use a for loop index from an asynchronous function.
I don't understand exactly what you're trying to accomplish so I'm not sure exactly what code to suggest. You have three values. Are you trying to do two loops, one to go through the array and one to process each array value? Can you explain better what the final output should be?
I'm guessing here, but if what you're trying to do is to call your function 8 times, then pause, then 2 times then pause, then 15 times, then be done, you can do this:
function runArray(arr, fn) {
// initialize array index - can't use for loop here with async
var index = 0;
function next() {
var cnt = +arr[index];
for (var i = 0; i < cnt; i++) {
fn(index, cnt);
}
// increment array index and see if there's more to do
++index;
if (index < arr.length) {
setTimeout(next, 400);
}
}
// start the whole process if the array isn't empty
if (arr.length) {
next();
}
}
var theArray = ['8','2','15'];
runArray(theArray, thefunction);
Working demo: http://jsfiddle.net/jfriend00/Loycmb3b/
FYI, if what you're putting in the array are meant to be used as numbers, it's better to put them in the array as numbers, not as strings. I've made my code work either way, but it's more efficient and just better code to use numbers when you mean numbers.
Ok, the title might make it sound easy (and maybe it is), but I can't figure out how to show values from an array, not just do a each with them, but put them on the site slowly, with a nice effect...
Like this Twitter widget, but from an array (maybe wait 2sec, and when throw another value from the array)?
My array contains another array, with 4 values, is it possible to show the first 2 values, wait about 2 sec, and then show the last 2 values? Now when the last two values is out (from the prev array), wait 2 more seconds, and show the next array (again with four values, first show 2, wait 2 sec, and show the next 2, and so on...)
You can do this easily using setInterval or a chained series of setTimeout. I tend to prefer the latter, which looks something like this:
function showValues(a, delay) {
var index = 0;
if (a.length !== 0) {
// If you want a delay before the first one:
setTimeout(showValue, delay);
// Alternately, if you want to show the first right away
// showValue();
}
// Function to show one value
function showValue() {
// Get the value
var value = a[index++];
/* ...show the value however you see fit... */
// Schedule next display, if any
if (index < a.length) {
// Schedule next run
setTimeout(showValue, delay);
}
}
}
Usage:
showValues(["value1", "value2", /* etc. */, 2000); 2000 = two seconds
Live example | source
Animation functions like fadeIn() make use of the jQuery FX queue so you can use .delay() in the call chain to delay the calling of these functions. In the following example one element is shown every second.
var data = ['foo', 'bar', 'foobar', 'moo', 'meow', 'xxx'];
var list = $('ul');
$.each(data, function(i, value) {
$('<li/>', { text: value }).hide().appendTo(list).delay(i * 1000).fadeIn();
});
Demo: http://jsfiddle.net/ThiefMaster/frW8s/