I have a bubble sort function that when the a swap occurs it should show it visibly. But after lots of approaches it continues executing the loops without waiting for the animation to stop. (we are only allowed to use JavaScript). Is there a way to tell the website to wait for the animation to complete. Here is a snippet of my code:
for (var i = 0; i < len; i++)
{
for (var j = 0, swapping, endIndex = len - i; j < endIndex; j++)
{
if (marksArr[j] > marksArr[j + 1])
{
//swap objects
/*(function() {
var k = 0,
action = function() {
document.getElementById(coursesKey[j]).style.top = (parseInt(document.getElementById(coursesKey[j]).style.top) + 1) + 'px';
document.getElementById(coursesKey[j + 1]).style.top = (parseInt(document.getElementById(coursesKey[j+1]).style.top) - 1) + 'px';
k++;
if (k < difMoves) {
setTimeout(action, 200);
}
};
setTimeout(action, 200);
})();*/
}
}
}
As #cookie monster already explained, you can't block loops in JavaScript and update the UI using setTimeout() at the same time (this is because JavaScript is essentially single-threaded).
The most elegant solution would be "recording" each animation step while the for loops process the array, and then run the animation afterwards, e.g. using setTimeout(). Some time ago, I've implemented this approach for a similar question on StackOverflow; here's a demo: JSFiddle
The code can also be simplified:
function swap(list, i1, i2) {
var tmp = list[i1];
list[i1] = list[i2];
list[i2] = tmp;
}
function animateBubblesort(list) {
var animationSteps = [];
// Sort array
for (var n = list.length; n > 1; --n) {
for (var i = 0; i < n-1; ++i) {
if (list[i] > list[i+1]) {
swap(list, i, i+1);
// Add new step (clone of current array) to "animation queue"
animationSteps.push(list.slice(0));
}
}
}
// Print animated steps (using setInterval() for simplicity)
var count = 0,
interval = setInterval(function(){
console.log(animationSteps[count]);
if (++count >= animationSteps.length) {
clearInterval(interval);
}
}, 250);
}
animateBubblesort([5,8,2,4,1,9,7,3,0,6]);
DEMO
Another (less intuitive) possibility would be to implement the algorithm without loops in order to get control over when the next step of the algorithm is executed (so you have time for your animation). Here's an example: DEMO 2
Related
I'm trying to create an image slider for a website, I want to learn why my array won't go back to zero after finishing the first iteration and keep on iterating. I want the slider to iterate on and on automatically.
function run() {
for (let i = 0; i < imgArray.length; i++) {
(function(e) {
setTimeout(function() {
if (i == imgArray.length) {
i = 0;
}
imgContainer.style.background = imgArray[i];
}, 3000 * e);
})(i);
};
imgContainer.style.height = '100vh';
}
The condition i == imgArray.length inside the loop is never true, since the loop runs until i < imgArray.length. You could use <= instead as Ali Abug Hijleh suggested, but I think it would be easier to maintain if you explicitly show the loop should run forever with while (true) and use the % operator to get the right index:
function run() {
let i = 0;
while (true) {
(function(e) {
setTimeout(function() {
imgContainer.style.background = imgArray[i % imgArray.length];
}, 3000 * e);
})(i);
};
imgContainer.style.height = '100vh';
++i;
}
i will never reach the imgArray.length because the loop only works when i is less than imgArray.length (since you use i < imgArray.length as the condition)
Try if (i == imgArray.length -1) { ... } instead.
That's because your for loop ends before you reset it to zero because you reset it to zero inside a setTimeout
im trying to bubble sort some information (by asking users which they prefer instead of compare)
when i use settimeout in callback function it only runs once at the first level of for loop.
(s changes by clicking on images
this is the main function we pass it an array as argument
function bubble_Sort(a)
{
var swapp;
var n = a.length-1;
var x=a;
do {
swapp = false;
for (var i=0; i < n; i++)
{
s=0;
limage.setAttribute('src','images/'+x[i]+'.jpg');
rimage.setAttribute('src','images/'+x[i+1]+'.jpg');
console.log('images changed');
check(i,function(){
if (s==0)
{
var temp = x[i];
x[i] = x[i+1];
x[i+1] = temp;
swapp = true;
}
});
}
n--;
} while (swapp);
return x;
}
and here is check function (callback to wait for user to click on one of images)
var check=function(p,callback){
setTimeout(function(){console.log('this is Timeout');},3000);
callback();
}
you don't need a set-timeout function to implement such functionality, your code has to wait for a click (asynchronous) event to continue it's computation. can get what you want with the help of generator functions.
here's a simple working sample, the code is mostly self descriptive, hope it helps!
// a generator function
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
function* bubbleSort(items) {
var length = items.length;
for (var i = 0; i < length; i++) {
for (var j = 0; j < (length - i - 1); j++) {
firstImageIndex = j;
secondImageIndex = j + 1;
updateUI(); //magic happens here
shouldBeSwapped = yield i; // wait for user to select a picture (no set timeout here)
if (shouldBeSwapped) {
let tmp = items[j];
items[j] = items[j + 1];
items[j + 1] = tmp;
}
}
}
updateUI(); // final update
}
Codepen Link Here !
I want to find elements with javascript with delay. In first step I made this and it works.
function mytag() {
var elements = document.getElementsByTagName('div');
for (var i=0, im=elements.length; im>i; i++) {
if (elements[i].className ==='hi'){
alert('found');
}}
}
In the second step, I made some changes to code to put delay between iteration. I followed this link but cannot make it to work . Whats wrong?
function mytag() {
var elements = document.getElementsByTagName('div');
for (var i=0, im=elements.length; im>i; i++) {
(function(i){
setTimeout(function(){
if (elements[i].className ==='hi'){
alert('found!');
}
}, 3000 * i);
}(i));
}
}
Here's an example of how you could add asynchronousity into your lookup function:
function findElements() {
var elements = document.getElementsByTagName('div');
var index = 0;
var findNext = function() {
var element = elements[index];
index++;
if (element.className === 'hi'){
// Item found
console.log('found:'+element);
}
if (index < elements.length) {
setTimeout(findNext, 100);
}
};
findNext();
}
findElements();
http://jsbin.com/zeseguribo/1/edit?html,js,console
function sleep(milliseconds) {
var start = new Date().getTime();
while (1) {
if ((new Date().getTime() - start) > milliseconds) {
break;
}
}
} // sleep end
then call sleep(3000) in your loop.
Edit: This is a blocking way of delay. For async, non-blocking delay, you may use a recursive function.
guys! I am a total newbie in javascript. I wrote my first program and already stuck with it's behavior:
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
if (i % 2 == 0) {
elements[i].src = "img.jpg";
for (var j = 0; j <= 1; j += 0.1) {
setTimeout(increase_opacity(elements[i], j), 2000);
// setTimeout(alert(j), 2000);
}
}
}
function increase_opacity(element, opacity) {
element.style.opacity = opacity;
}
I can not see that opacity changes, but under debugger it does, so setTimeout just doesn't work. If I uncomment // setTimeout(alert(j), 2000); I can see both opacity change and alert message on every cycle step. Why is that?
setTimeOut accepts a function, and what you were doing is passing it the result of a call to increase_opacity. Code below should work for you. Notice how increase_opacity is wrapped inside of the anonymous function definition.
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
if (i % 2 == 0) {
elements[i].src = "img.jpg";
for (var j = 0; j <= 1; j += 0.1) {
(function(element, opacity) {
setTimeout(function() { increase_opacity(element, opacity) } , 2000);
})(elements[i], j);
// setTimeout(alert(j), 2000);
}
}
}
Here is the doc on setTimeOut http://www.w3schools.com/jsref/met_win_settimeout.asp
You need to pass a function to setTimeout(). What you are doing is calling increase_opacity immediately and then passing the return value to setTimeout().
Since you want to call the function with particular arguments, you can use a closure to record the current values of i and j:
setTimeout((function(a, b) {
return function() {
increase_opacity(a, b);
};
})(elements[i], j), 2000);
Are you aware that setTimeout() is not a blocking call? Your script does not stop at the setTimeout() function, but continues its execution. So here's what's happening here:
1) Your code starts the loop;
2) When it enters a setTimeout(), it launches the timer it and goes ahead;
3) Repeat step 2 for every element; (Please note that steps 2 and 3 are executed in a few ms)
4) All the timers expires, all the instructions are executed all at once.
To avoid this, you should write something like:
var elements = document.getElementsByTagName("img");
for (var i = 0; i < elements.length; i++) {
if (i % 2 == 0) {
elements[i].src = "img.jpg";
setTimeout(function(){increase_opacity(elements[i],0);}, 2000);
}
}
function increase_opacity(element, opacity) {
element.style.opacity = opacity;
if (opacity < 1) setTimeout(function(){increase_opacity(element, opacity+0.1);},2000);
}
NB: When dealing with setTimeout() you should avoid that notation when calling function with parameters. You should always use this:
setTimeout(function(){yourFunctionName(arg1, arg2, arg3);});
NB 2: setTimeout(alert(j),2000); is not even waiting 2 seconds to appear. Like I said above, you should use the anonymous function when dealing with setTimeout(). It's probably giving the impression it's working because of this, but it's a sort of glitch IMO.
Completely stuck.
I have seen this popular solution for adding delays between iterations of a loop (https://stackoverflow.com/a/3583740), but it only seems to work for a single loop (i.e not nested).
This is the pseudo code,
for (var i = 0; i < max; i++){
for (var j = 0; j < max; j++){
pause(1000); // ideally would just elegantly pause all execution
}
}
I am trying to use setTimeout (unless other options exist!) but I can't seem to wrap my head around how to set it up.
Context - It should pause long enough for an animation to occur. (A different animation occurs depending on the values of i and j ).
As it has been said, you can't really pause execution in javascript. Though a trick could be to collect the instructions in a delayed queue which executes itself fifo. E.g.:
// helper function
var delayed = (function() {
var queue = [];
function processQueue() {
if (queue.length > 0) {
setTimeout(function () {
queue.shift().cb();
processQueue();
}, queue[0].delay);
}
}
return function delayed(delay, cb) {
queue.push({ delay: delay, cb: cb });
if (queue.length === 1) {
processQueue();
}
};
}());
your example, rewritten:
var i = 0, j, max = 3;
for (; i < max; i += 1) {
for (j = 0; j < max; j += 1) {
// add function to the queue, shadowing i/j with an IIFE:
delayed(1000, function(i, j) {
return function() {
console.log(i, j);
};
}(i, j));
}
}
demo: http://jsbin.com/kadepage/1/
This will work as nested for loop with delay.
var i = o = 0;
var max = 10;
var delay = 1000;
function rec()
{
console.log("outerloop"+o);
var inner = setInterval(function(){
console.log("innerloop"+i);
console.log(new Date());
i++;
if(i==max)
{
clearTimeout(inner);
var outer = setTimeout(function(){
o++;
i=0;
if(o==max)
{
return;
}
clearTimeout(outer);
rec();
},delay);
}
},delay);
}
rec();