Why is only the last object's function getting called here? - javascript

Wow. I finally figured about what is causing the bug, but I can't figure out why. I have an object with a property (excuse the massive code dump)
// relatives second indices in the video to events
// that are called when the video reaches that second
this.PausePoints = [
{
sec: 10,
name: "Point number 1",
passed: false,
func: (function(that) {
this.$layer = that.GetLayerElement(10);
this.$layer.hide();
this.to = function () {
that.videlem.pause(); // pause video
$(window).resize(); // re-proportion stuff
// point the 3 mouse pointers
var $mptrs = this.$layer.find('.filmstrip-pointer');
for (var i = 0; i < $mptrs.length; ++i) {
(function (j) {
setTimeout(function () {
Point($mptrs.eq(j));
}, j * 1000);
})(i);
}
};
// attach click event to 3 sections
$clickRegions = $layer.find('div.click-region');
$clickRegions.click(function(){
$clickRegions.removeClass('clicked');
$(this).addClass('clicked');
});
this.away = function () {
this.$layer.hide();
}
// attach event to next button
$layer.find('.next-btn').click(function(){
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
},
{
sec: 26,
name: "Point number 2",
passed: false,
func: (function(that) {
this.$layer = that.GetLayerElement(26);
this.$layer.hide();
this.to = function () {
// loop video between 0:26-0:31
this.loop = setInterval(function () {
that.videlem.currentTime = 26;
that.videlem.play();
}, 5000);
// point the 3 mouse pointers
var $mptrs = this.$layer.find('.filmstrip-pointer');
for (var i = 0; i < $mptrs.length; ++i) {
(function (j) {
setTimeout(function () {
Point($mptrs.eq(j));
}, j * 1000);
})(i);
}
this.$layer.show();
}
// separate pargraph words by spans
this.$layer.find('p').each(function () {
var spanned = $(this).text().split(" ").map(function (w) { return '<span class="word">' + w + '</span>'; }).join(" ");
$(this).html(spanned);
});
// add event click event on headlines
var timeouts = [];
this.$layer.find('h3').click(function () {
// clear any current 'showing' animations
timeouts.forEach(function(t){ clearTimeout(t); });
timeouts = [];
// unshow all words on the slide
this.$layer.find('span.word').removeClass('shown');
// show all words associated with the headline that was clicked
var $wspans = $(this).closest('.tower-layer').find('span.word');
for ( var i = 0; i < $wspans.length; ++i )
{
(function(j){
timeouts.push(setTimeout(function(){
$wspans.eq(j).addClass('shown');
},j*100));
})(i);
}
}.bind(this));
this.away = function () {
clearInterval(this.loop);
this.$layer.find('span.word').removeClass('shown');
$layer.hide();
that.videlem.currentTime = 31;//go to end of loop
};
// set action of "Next" button
this.$layer.find('.next-btn').click(function () {
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
},
{
sec: 38,
name: "Point number 3",
passed: false,
func: (function(that) {
this.$layer = that.GetLayerElement(38);
this.$layer.hide();
this.to = function ( ) {
// loop video between 0:38-0:43
this.loop = setInterval(function () {
that.videlem.currentTime = 38;
that.videlem.play();
}, 5000);
this.$layer.show();
}
this.away = function(){
clearInterval(this.loop);
this.$layer.hide();
};
this.$layer.find('.next-btn').click(function(){
that.videlem.currentTime = 43;
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
},
{
sec: 47,
name: "Point number 4",
passed: false,
func: (function(that){
this.$layer = that.GetLayerElement(47);
this.$layer.hide();
this.to = function ()
{
// loop video between 0:47-0:52
this.loop = setInterval(function() {
that.videlem.currentTime = 47;
that.videlem.play();
}, 5000);
// show layer
this.$layer.show();
}
this.away = function () {
clearInterval(this.loop);
this.$layer.hide();
};
this.$layer.find('.next-btn').click(function () {
that.videlem.currentTime = 52;
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
},
{
sec: 57,
name: "Point number 5",
passed: false,
func: (function(that){
this.$layer = that.GetLayerElement(57);
// hide initially
this.$layer.hide();
this.to = function ()
{
// loop video between 0:57-1:02
this.loop = setInterval(function () {
that.videlem.currentTime = 57;
that.videlem.play();
}, 5000);
this.$layer.show();
}
this.away = function(){
clearInterval(this.loop);
$layer.hide();
};
this.$layer.find('.next-btn').click(function () {
that.videlem.currentTime = 62;
this.away();
that.videlem.play();
}.bind(this));
return this;
})(this)
}
];
and what I'm noticing is that when I try to call any of the to functions it always calls the one in the last element of the array.
For example,
VidHandler.PausePoints[0].func.to()
calls
this.to = function ()
{
// loop video between 0:57-1:02
this.loop = setInterval(function () {
that.videlem.currentTime = 57;
that.videlem.play();
}, 5000);
this.$layer.show();
}
instead of the expected
this.to = function () {
that.videlem.pause(); // pause video
$(window).resize(); // re-proportion stuff
// point the 3 mouse pointers
var $mptrs = this.$layer.find('.filmstrip-pointer');
for (var i = 0; i < $mptrs.length; ++i) {
(function (j) {
setTimeout(function () {
Point($mptrs.eq(j));
}, j * 1000);
})(i);
}
};
Why is this happening and how can I fix it?

The problem is you're trying to assign something to func using an immediately invoked function expression (IIFE). Those IIFEs are executed before the object is constructed, meaning this refers to something else. Your code can basically be broken down like this:
this.to = function() {
// version for "Point number 1"
};
this.to = function() {
// version for "Point number 2"
// notice that you're overwriting the previous one
};
// repeat for all points
var self = this;
this.PausePoints = [
{
name: "Point number 1",
func: self
},
// repeat for all points
];
So what you're actually doing is assigning a to value to the same object that has the PausePoints property.

Related

Images navigation infinite loop

This is my code for right and left navigation.
How can I add infinite loop in this:
if (i < this.sindex) { //slide to right
_old.addClass('right');
setTimeout(function () {
_old.removeClass('right sel anim')
}, 300);
_new.removeClass('anim right').addClass('sel left');
setTimeout(function () {
_new.addClass('anim').removeClass('left')
}, 5);
} else if (i > this.sindex) { //slide to left
_old.addClass('left');
setTimeout(function () {
_old.removeClass('left sel anim')
}, 300);
_new.removeClass('anim left').addClass('sel right');
setTimeout(function () {
_new.addClass('anim').removeClass('right')
}, 5);
}
It's a sumogallery plugin which doesn't have infinite loop function.
Not sure if you are using any plugins. However, you can implement your own infinite navigation easily.
In order to loop infinitely in a non-blocking way you can use setTimeout and call your handler recursively.
Infinite loop implementation:
class InfiniteLooper {
constructor(arr, handler, options){
this.arr = arr;
this.index = 0;
this.options = options;
this.handler = handler;
this.t1 = null
this.t2 = null
}
recur() {
var that = this;
if(this.index < this.arr.length){
this.t1 = setTimeout(this.handler(this.arr[this.index]), 0);
this.index ++
if(this.options && this.options.circular && this.index == this.arr.length) {
this.index = 0;
}
this.t2 = setTimeout(function() {
that.recur()
}, 0);
}
}
run() {
this.recur()
}
stop() {
clearTimeout(this.t1)
clearTimeout(this.t2)
}
}
const array = [1,2,3,4,5]
const IL = new InfiniteLooper(array, console.log, {circular:true});
IL.run()
// Execute some more code
console.log('Non blocking!');
console.log('Do some math', 100*9);
const t = setInterval(()=>{
console.log('Do some more math in every 1 seconds', Math.random(1,4));
}, 1000)
// stop the loop after 10 sec
setTimeout(()=>{
IL.stop()
clearInterval(t)
}, 10000)
I wrote in detail here https://medium.com/#mukeshbiswas/looping-infinitely-in-a-non-blocking-way-2edca27bc478. See if this helps.

How to pass unspecified number of parameters to setInterval

I need to create an interval wrapper to track if it has been cleared.
The number of parameters to pass to the interval callback should be variable. So this is the code (not working) I implemented to test it:
function MyInterval() {
var id = setInterval.apply(this, arguments); // NOT VALID!!
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
var x = 2;
var y = 3;
var fn = function() {
x = x + y;
console.log(x);
};
var interval = new MyInterval(fn, 5000, x, y);
Within the call to setInterval, this must refer to the global object, so instead of this, you want window in your constructor:
var id = setInterval.apply(window, arguments);
// here -------------------^
(or in loose mode you could use undefined or null.)
Then it works, at least on browsers where setInterval is a real JavaScript function and therefore has apply:
function MyInterval() {
var id = setInterval.apply(window, arguments);
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
var x = 2;
var y = 3;
var fn = function() {
x = x + y;
log(x);
};
var interval = new MyInterval(fn, 500, x, y);
setTimeout(function() {
interval.clear();
}, 3000);
function log(msg) {
var p = document.createElement('p');
p.appendChild(document.createTextNode(msg));
document.body.appendChild(p);
}
Note, though, that host-provided functions are only required to be callable, they are not required to inherit from Function.prototype and so they're not required/guaranteed to have apply. Modern browsers ensure they do, but earlier ones (IE8, for instance) did not. I can't speak to how well-supported apply is on setInterval.
If you need to support browsers that may not have it, just to use your own function:
function MyInterval(handler, interval) {
var args = Array.prototype.slice.call(arguments, 2);
var tick = function() {
handler.apply(undefined, args);
};
var id = setInterval(tick, interval);
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
This also has the advantage that it works even on browsers that don't support additional args on setInterval (fairly old ones).
Example:
function MyInterval(handler, interval) {
var args = Array.prototype.slice.call(arguments, 2);
var tick = function() {
handler.apply(undefined, args);
};
var id = setInterval(tick, interval);
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
var x = 2;
var y = 3;
var fn = function() {
x = x + y;
log(x);
};
var interval = new MyInterval(fn, 500, x, y);
setTimeout(function() {
interval.clear();
}, 3000);
function log(msg) {
var p = document.createElement('p');
p.appendChild(document.createTextNode(msg));
document.body.appendChild(p);
}
You might be tempted to use the new ES2015 spread operator:
var id = setInterval(...arguments);
...but note that if you transpile (and right now you'd have to), it ends up being an apply call, and so you have the issue of whether apply is supported.
I suggest that you pass an "options" parameter to your timeout.
var MyInterval = (function(window) {
return function(callbackFn, timeout, options) {
var id = setInterval.apply(window, arguments);
this.cleared = false;
this.clear = function() {
this.cleared = true;
clearInterval(id);
};
}
}(window));
var fn = function(opts) {
opts.x += opts.y;
console.log('x = ', opts.x);
};
var opts = {
x: 2,
y: 3
};
var ms = 5000;
var interval = new MyInterval(fn, ms, opts);
// Bootstrap a custom logger. :)
console.log = function() {
var logger = document.getElementById('logger');
var el = document.createElement('LI');
el.innerHTML = [].join.call(arguments, ' ');
logger.appendChild(el);
logger.scrollTop = logger.scrollHeight;
}
body{background:#7F7F7F;}h1{background:#D7D7D7;margin-bottom:0;padding:0.15em;border-bottom:thin solid #AAA;color:#444}#logger{height:120px;margin-top:0;margin-left:0;padding-left:0;overflow:scroll;max-width:100%!important;overflow-x:hidden!important;font-family:monospace;background:#CCC}#logger li{list-style:none;counter-increment:step-counter;padding:.1em;border-bottom:thin solid #E7E7E7;background:#FFF}#logger li:nth-child(odd){background:#F7F7F7}#logger li::before{content:counter(step-counter);display:inline-block;width:1.4em;margin-right:.5em;padding:.25em .75em;font-size:1em;text-align:right;background-color:#E7E7E7;color:#6A6A6A;font-weight:700}
<h1>Custom HTML Logger</h1><ol id="logger"></ol>
I created a utility function rather than a constructor to solve your issue.
function Wrapper(delay) {
var isCleared,
intervalId,
intervalDelay = delay || 5e3; // default delay of 5 sec
function clear() {
if (!isCleared) {
console.log('clearing interval');
isCleared = true;
clearInterval(intervalId);
}
}
function setUpInterval(callback){
var params = [].slice.call(arguments, 1);
if (!callback) {
throw new Error('Callback for interval expected');
}
params.unshift(intervalDelay);
params.unshift(callback);
intervalId = setInterval.apply(null, params);
}
return {
setUp : setUpInterval,
clear : clear
}
}
function intervalCallback() {
console.log([].slice.call(arguments).join(','));
}
var wrapper = Wrapper(1e3); // create wrapper with delay for interval
console.log('test case 1');
wrapper.setUp(intervalCallback, 'params', 'to', 'callback');
// call clear interval after 10sec
setTimeout(function() {
wrapper.clear();
}, 10e3);
Hope this helps.

Scoping issue for nested class

I cannot manage to access the method of the nested class. This is what I have tried so far. The main issue is calling TimerTask.execute(). The error states that the task is undefined.
Uncaught TypeError: undefined is not a function
The program should display an increment number for ten runs on a timer.
var TimerTask = new Class({
initalize: function (options) {
this.counter = options.counter;
this.run = options.run;
this.onComplete = options.complete;
this.done = false;
},
execute : function() {
var me = this;
this.counter--;
if (this.done === false || this.counter <= 0) {
this.done = true;
me.onComplete.call(me);
} else {
me.run.call(me);
}
}
});
var Timer = new Class({
initialize: function (options) {
this.id = 0;
this.running = true;
this.count = 0;
this.delay = options.delay || 1000;
this.tasks = options.tasks || [];
},
start: function () {
var me = this;
me.id = setInterval(function tick() {
if (!me.running) return;
for (var i = 0; i < me.tasks.length; i++) {
me.tasks[i].execute();
}
me.count++;
}, this.delay);
},
pause: function pause() {
this.running = false;
return this;
},
run: function run() {
this.running = true;
return this;
},
stop: function stop() {
clearInterval(this.id);
this.stopped = true;
return this;
},
schedule: function (task) {
this.tasks.push(task);
}
});
var i = 0;
var t1 = new Timer({
delay: 1000,
tasks: [
new TimerTask({
run: function () {
document.getElementById('output').innerHTML = parseInt(i++, 10);
},
onComplete: function () {
alert('DONE!');
},
counter: 10
})]
});
t1.start();
<script src="//cdnjs.cloudflare.com/ajax/libs/mootools/1.5.0/mootools-core-full-compat.min.js"></script>
<div id="output"></div>
Oh, wow. Cannot believe that no one caught the typo in TimerTask. No wonder I kept getting the prototype instead of the instance. I spelled "initialize" without the 3rd "i"...
Anyways, I fixed the issue and the code is glorious.
Note: I was using this as the basis for my timer -- https://gist.github.com/NV/363465
var TimerTask = new Class({
initialize: function (options) {
this.counter = options.counter || 0;
this.run = options.run;
this.onComplete = options.onComplete;
this.active = true;
this.isInfinite = this.counter === 0;
},
execute: function () {
if (!this.isInfinite && this.counter === 0) {
this.active = false;
if (this.onComplete !== undefined) {
this.onComplete();
}
} else {
this.run();
if (!this.isInfinite) {
this.counter--;
}
}
},
isActive: function () {
return this.active;
}
});
var Timer = new Class({
initialize: function (options) {
this.id = 0;
this.running = true;
this.count = 0;
this.delay = options.delay || 1000;
this.tasks = options.tasks || [];
Timer.all.push(this);
},
start: function () {
var me = this;
me.id = setInterval(function tick() {
if (!me.running) return;
for (var i = 0; i < me.tasks.length; i++) {
var task = me.tasks[i];
if (task.isActive()) {
task.execute();
} else {
console.log('Task is complete...');
}
}
me.count++;
}, this.delay);
},
pause: function pause() {
this.running = false;
return this;
},
run: function run() {
this.running = true;
return this;
},
stop: function stop() {
clearInterval(this.id);
this.stopped = true;
return this;
},
schedule: function (task) {
this.tasks.push(task);
}
});
Timer.extend({
all : [],
pause : function pause() {
var all = Timer.all;
for (var i = 0; i < all.length; i++) {
all[i].pause();
}
return all;
},
run : function run() {
var all = Timer.all;
for (var i = 0; i < all.length; i++) {
all[i].run();
}
return all;
},
stop : function stop() {
var all = Timer.all;
for (var i = 0; i < all.length; i++) {
all[i].stop();
}
return all;
}
});
function print(id, value) {
document.getElementById(id).innerHTML = value;
}
var i = 0;
var t1 = new Timer({
delay: 100,
tasks: [
new TimerTask({
run: function () {
print('output1', String.fromCharCode(65 + i));
},
onComplete: function () {
console.log('Task 1 complete...');
},
counter: 26
}),
new TimerTask({
run: function () {
print('output2', parseInt(i++, 10));
}
})]
});
t1.start();
// After 4 seconds, stop all timers.
setTimeout(function() {
Timer.stop();
}, 4000);
<script src="//cdnjs.cloudflare.com/ajax/libs/mootools/1.5.0/mootools-core-full-compat.min.js"></script>
<div id="output1"></div>
<div id="output2"></div>

how to clear all javascript Timeouts?

i have a loop function that in first 5 seconds it runs social1() and in second 5 seconds it runs social2() then loop ...
i have 2 hover functions too
i need clear all active timeouts because when i hover on images (.social1 & .social2), i can see that multiple timeouts are running
how to fix this?
function social1() {
$('.social1').fadeTo(500, 1);
$('.social2').fadeTo(500, 0.5);
timeout = setTimeout(function() {
social2();
}, 5000);
}
function social2() {
$('.social1').fadeTo(500, 0.5);
$('.social2').fadeTo(500, 1);
timeout = setTimeout(function() {
social1();
}, 5000);
}
$(document).ready(function ()
{
social1();
$('.social1').hover(
function () {
window.clearTimeout(timeout);
social1();
},
function () {
timeout = setTimeout(function() {
social2();
}, 5000);
}
);
$('.social2').hover(
function () {
window.clearTimeout(timeout);
social2();
},
function () {
timeout = setTimeout(function() {
social1();
}, 5000);
}
);
__EDIT__
To manage a collection of timeouts (and intervals), you could use following snippet.
This will allow to clear any timeouts or intervals set anywhere in code, although, you have to set this snippet before setting any timeout or interval. Basically, before processing any javascript code or external script which uses timeout/interval.
JS:
;(function () {
window.timeouts = {},
window.intervals = {},
window.osetTimeout = window.setTimeout,
window.osetInterval = window.setInterval,
window.oclearTimeout = window.clearTimeout,
window.oclearInterval = window.clearInterval,
window.setTimeout = function () {
var args = _parseArgs('timeouts', arguments),
timeout = window.osetTimeout.apply(this, args.args);
window.timeouts[args.ns].push(timeout);
return timeout;
},
window.setInterval = function () {
var args = _parseArgs('intervals', arguments),
interval = window.osetInterval.apply(this, args.args);
window.intervals[args.ns].push(interval);
return interval;
},
window.clearTimeout = function () {
_removeTimer('timeouts', arguments);
},
window.clearInterval = function () {
_removeTimer('intervals', arguments);
},
window.clearAllTimeout = function () {
_clearAllTimer('timeouts', arguments[0]);
},
window.clearAllInterval = function () {
_clearAllTimer('intervals', arguments[0]);
};
function _parseArgs(type, args) {
var ns = typeof args[0] === "function" ? "no_ns" : args[0];
if (ns !== "no_ns")[].splice.call(args, 0, 1);
if (!window[type][ns]) window[type][ns] = [];
return {
ns: ns,
args: args
};
}
function _removeTimer(type, args) {
var fnToCall = type === "timeouts" ? "oclearTimeout" : "oclearInterval",
timerId = args[0];
window[fnToCall].apply(this, args);
for (var k in window[type]) {
for (var i = 0, z = window[type][k].length; i < z; i++) {
if (window[type][k][i] === timerId) {
window[type][k].splice(i, 1);
if (!window[type][k].length) delete window[type][k];
return;
}
}
}
}
function _clearAllTimer(type, ns) {
var timersToClear = ns ? window[type][ns] : (function () {
var timers = [];
for (var k in window[type]) {
timers = timers.concat(window[type][k]);
}
return timers;
}());
for (var i = 0, z = timersToClear.length; i < z; i++) {
_removeTimer(type, [timersToClear[i]]);
}
}
}());
How to use it:
Set timeout(s)/interval(s) as usual:
var test1 = setTimeout(function(){/**/, 1000);
var test2 = setTimeout(function(){/**/, 1000);
Then you could use to clear both:
clearAllTimeout(); // clearAllInterval(); for intervals
This will clear both timeouts (test1 & test2)
You can use some namespaces to clear only specific timers, e.g:
// first (optional) parameter for setTimeout/setInterval is namespace
var test1 = setTimeout('myNamespace', function(){/**/, 1000); // 'myNamespace' is current namespace used for test1 timeout
var test2 = setTimeout(function(){/**/, 1000); // no namespace used for test2 timeout
Again, clearAllTimeout(); will clear both timeouts. To clear only namespaced one, you can use:
clearAllTimeout('myNamespace'); // clearAllInterval('myNamespace'); for namespaced intervals
This will clear only test1 timeout
You could for some reason wish to delete non namespaced timeouts only. You could then use:
clearAllTimeout('no_ns'); // clearAllInterval('no_ns'); for non namespaced intervals only
This will clear only test2 timeout in this example
See jsFiddle DEMO
__END of EDIT__
Old post specific to opening question here:
You could try that:
var timeouts = [];
timeouts.push(setTimeout(function() {
social2();
}, 5000));
timeouts.push(setTimeout(function() {
social1();
}, 5000));
//etc...
function clearAllTimeouts(){
for(var i = 0, z = timeouts.length; i < z; i++)
clearTimeout(timeouts[i]);
timeouts = [];
}
UPDATED following David Thomas comment
var timeouts = {'social' : [], 'antisocial' : []};
//a social timeout
timeouts.social.push(setTimeout(function() {
social1();
}, 5000));
//an anti-social timeout
timeouts.antisocial.push(setTimeout(function() {
antisocial1();
}, 5000));
function clearTimeouts(namespace){
for(var i = 0, z = timeouts[namespace].length; i < z; i++)
clearTimeout(timeouts[namespace][i]);
timeouts[namespace] = [];
}
//usage e.g
clearTimeouts("social");
//Incase if you are looking for full fledged code
var dict = {};
function checkForIntervals(id){
var index = index;
var result = findOrAddProperty(id);
if(result.length != 0){
clearTimeoutsFor(id);
}
dict[id].push(setTimeout(function(){alertFunc(id,index);}, 60000));
};
// to clear specific area timeout
function clearTimeoutsFor(namespace){
for(var i = 0, z = dict[namespace].length; i < z; i++)
clearTimeout(dict[namespace][i]);
dict[namespace] = [];
}
to clear all timeouts
function clearAllTimeOuts(){
for (key in dict) {
for(var i = 0, z = dict[key].length; i < z; i++)
clearTimeout(dict[key][i]);
dict[key] =[];
}
};
function findOrAddProperty(str){
var temp = [];
for (key in dict) {
if(key == str){
if (dict.hasOwnProperty(key)) {
temp = dict[key];
break;
}
}
}
if(temp.length == 0){
dict[str] = [];
}
return temp;
};
function alertFunc(id,index) {
jQuery(document).ready(function($) {
do the ajax call here after 1 min
});
};

Javascript Animation Routine

I am doing some simple animation in Javascript. In light of the recent earthquake on the East Coast, I have implemented an earthquake effect whereby a table of information jostles around for a while when you click a button. I want the jostling to start out strong, and then peter out.
I have a utility function that repeatedly calls another function at a set interval. Then it calls a second function when it is all done calling the first function a bunch of times. This is so that you can schedule something to occur when the animation is over. Here is the code for it:
function countIterate(timeout, count, func1, func2)
{
if (count > 0) {
func1();
setTimeout(function() { countIterate(timeout, --count, func1, func2); }, timeout);
}
else
func2();
}
Here is the earthquake routine:
function earthQuake()
{
console.log("earthQuake()");
$("table").css("position", "relative");
var quake = function(magnitude)
{
var top = Math.floor(Math.random() * (2 * magnitude + 1)) - magnitude;
var left = Math.floor(Math.random() * (2 * magnitude + 1)) - magnitude;
$("table").css("top", top).css("left", left);
}
var func = new Array();
func[0] = function() {};
for (var i = 1; i <= 4; i++) {
func[i] = function() { countIterate(35, 40, function() { quake(i); }, func[i-1]); };
console.log(func[i]);
}
func[4]();
}
Unfortunately, I am getting an infinite earthquake loop.
If I hard-code things instead of the for loop:
var func0 = function() {};
var func1 = function() { countIterate(35, 40, function() { quake(1); }, func0); };
var func2 = function() { countIterate(35, 40, function() { quake(2); }, func1); };
var func3 = function() { countIterate(35, 40, function() { quake(3); }, func2); };
var func4 = function() { countIterate(35, 40, function() { quake(4); }, func3); };
func4();
it works fine. But this is an ugly solution.
By the way, here is the console.log() output from the first (more elegant, but broken) solution:
function () { countIterate(35, 40, function() { quake(i); }, func[i-1]); }
function () { countIterate(35, 40, function() { quake(i); }, func[i-1]); }
function () { countIterate(35, 40, function() { quake(i); }, func[i-1]); }
function () { countIterate(35, 40, function() { quake(i); }, func[i-1]); }
If there is some library that will take care of this sort of thing, please let me know, but I want to get this version working anyway as a learning experience.
The function:
function() { countIterate(35, 40, function() { quake(i); }, func[i-1]); }
is always executed with i = 5 because by the time func[4](); is reached the for loop has already completed. This can be easily shown to be the problem by binding the value of i to the function by returning the function from another:
(function(i) {
return function() {
countIterate(35, 40, function() { quake(i); }, func[i-1]);
};
}(i))
For further explanation, you can refer to answers to the many other "for loop problem" questions that have been asked here, including the top answer for Javascript infamous Loop issue?.

Categories

Resources