Why it printing last element - javascript

I'm trying this javascript code
var txt = "", txtLen = 0, elem='';
var speed=90;
function write( obj ) {
txt = obj.str;
speed = obj.speed;
elem = obj.elem;
txtLen = txt.length;
setTimeout("loop()", 300);
}
var c=0;
function loop() {
if( c <= txtLen ){
document.getElementById(elem).innerHTML+=txt.charAt(c);
c++;
setTimeout("loop()", speed);
} else {
c=0;
}
}
but in html when i call write function two time its prints only last one, like this-
<font id="o"></font><br>
<font id="oo"></font>
<script>
write({
elem:'o',
speed:90,
str:'Hello'
});
write({
elem:'oo',
speed:90,
str:'World'
});
</script>
Can anyone tell me please, where the error is??

This should work
function loop(obj, c) {
if( c <= obj.str.length ){
document.getElementById(obj.elem).innerHTML += obj.str.charAt(c);
setTimeout(loop, obj.speed, obj,c+1);
}
}
function write( obj ) {
setTimeout( loop, 300, obj, 0);
}
The setTimeout function optionally can take variables that will be passed to the callback function

Because you are executing the printing asyncronously, so when you print the first one, the second one already modified your vars.
What you are facing is called impure functions.
Impure functions
All of them which modifies its external
environment, usually called side effect.
Any function that uses a non-local variable is potentially impure, for example:
function impure(x) { return x + a; }
The idea to solve it is transform your impure functions into pure functions. What does it mean? A pure function is a function which does not modify its external enviromnent.
Pure functions
All of them which does not modify its external
environment, for example:
function pure(x, a) { return x + a; }
Below you have your example working:
<font id="o"></font>
<br>
<font id="oo"></font>
<script>
function write( obj ) {
setTimeout(() => {
loop(obj, 0);
}, 300);
}
function loop(obj, c) {
if( c <= obj.str.length ){
document.getElementById(obj.elem).innerHTML += obj.str.charAt(c);
setTimeout(() => {
loop(obj, c + 1);
}, obj.speed);
}
}
write({
elem:'o',
speed:90,
str:'Hello'
});
write({
elem:'oo',
speed:90,
str:'World'
});
</script>
As you see, loop function takes nothing from its external environment any more.
Sharing vars is not a good option when you are working asyncronously.
Hope It helps you.

Related

Run the function only once at the same time

I have the following problem. First of all my code so far:
function Auktionator() {
this.versteigern = function(objekt) {
for(var i = 1; i <=3; i++) {
setTimeout(function(x) { return function() { console.log(objekt + " zum " + x); }; }(i), 1000*i);
}
};}
Now I want that only one Auktionator object can run the function at the same time, but I don´t know, how to do it.
Keep track of the number of timeouts running and use a guard clause to prevent concurrent runs.
function Auktionator() {
this.versteigern = function (objekt) {
if (Auktionator.LOCKS > 0) {
console.log('running');
return;
}
for (var i = 1; i <= 3; i++) {
Auktionator.LOCKS++;
setTimeout(function (x) {
return function () {
console.log(objekt + " zum " + x);
Auktionator.LOCKS--;
};
}(i), 1000 * i);
}
};
}
Auktionator.LOCKS = 0;
new Auktionator().versteigern()
new Auktionator().versteigern()
The hacky workaround would be to add a variable isRunning:
// Static variable shared by all instance
Auktionator.isRunning = false;
Then, when you start executing, you check if Auktionator.isRunning === false, set it to true and set it back to false when you're done.
You have a great variety of options to execute code after some async calls:
Promises, some libraries or some awesome stuff brought by ES6.
You could have a global variable
var function_in_use = 0
and then add
function_in_use = 1 at the very beginning of the function's contents and add function_in_use = 0 immediately before the return statement. You could then wrap the entire contents in an if statement: if (!function_in_use) { ....
I don't know if this would suit your particular needs. This would be similar to how #import "filename" statements work in the C language.

Function naming and launching

Why this is working:
JS
$(gallery_1 + " .gallery_comandi #next").click(function avanti_gallery() {
contatore ++;
if (contatore > tot_immagini) {
contatore = 1;
cambia_immagine ();
}
else { cambia_immagine ();
};
});
And this isn't? This second cose executes the function. Why? Shouldn't it be lauched by a command like avanti_gallery()?
JS
function avanti_gallery() {
contatore ++;
if (contatore > tot_immagini) {
contatore = 1;
cambia_immagine ();
}
else { cambia_immagine ();
};
};
$(gallery_1 + " .gallery_comandi #next").click(avanti_gallery());
You're passing the result, not the reference to the function.
Try this
$(gallery_1 + " .gallery_comandi #next").click(avanti_gallery);
With
$(gallery_1 + " .gallery_comandi #next").click(avanti_gallery());
...you're calling the function and passing the return value into click, exactly the same as foo(bar()) calls bar and passes the return value into foo.
Ditch the ():
$(gallery_1 + " .gallery_comandi #next").click(avanti_gallery);
// Here -----------------------------------------------------^
The reason your first one works is that you're only defining, not calling, the function. The () after the name in that expression are just part of the definition (specifically, part of the named function expression), whereas the () in the second example are actually calling the function.
It may be easier to tell the difference if we remove click from the picture:
Your first one:
var f = function avanti_gallery() {
contatore ++;
if (contatore > tot_immagini) {
contatore = 1;
cambia_immagine ();
}
else { cambia_immagine ();
};
};
(I need the var f = at the beginning or the named function expression changes into a function declaration, which although it looks the same is quite different, and it shows visually that we're using the resulting function reference)
Your second one:
var f = avanti_gallery();

Is there a better way to do callback chaining in javascript?

I wrote a callback helper, that lets me group multiple callbacks into one function variable:
function chainCallbacks() {
var callbacks = arguments;
return function () {
for(var i = 0; i < callbacks.length; i++) {
if(callbacks[i] != null) {
callbacks[i].apply(null, arguments);
}
}
};
}
this works, but I'm wondering if there are any javascript libraries that provide the same functionality? or even better, something that simulates the .NET "event" pattern?
myEvent+=myCallback;
I have modified your chainCallbacks function. You can test below code in JS console (I'm using Chrome -works fine), and check the result.
var result = 0;
function a() {
result += 5;
console.log(result);
_next();
}
function b() {
result += 10;
console.log(result);
_next();
}
function c() {
result += 20;
console.log(result);
_next();
}
function chainCallbacks() {
var _this = this;
var _counter = 0;
var _callbacks = arguments;
var _next = function() {
_counter++;
if(_counter < _callbacks.length) {
_callbacks[_counter].apply(_this);
}
};
_this._next = _next;
return function() {
if(_callbacks.length > 0) {
_callbacks[0].apply(_this);
}
};
}
var queue = chainCallbacks(a, b, c);
queue();
Idea is simple - you call _next() whenever your callback function has finished executing, and you want to jump to another. So you can call _next() e.g. after some jQuery animation as well, and this way you will preserve the order of the functions.
If you want to replace a callback with one that calls the original as well as some others, I'd probably just do something like this:
Requirejs.config.callback = function(orig) {
var fns = [orig, first, second, third];
return function() {
fns.forEach(function(fn) { fn.apply(null, this); }, arguments);
};
}(Requirejs.config.callback);
But if you're doing this often, I think your solution will be as good as it gets. I don't see need for a library.
Requirejs.config.callback = chainCallbacks(Requirejs.config.callback, first, second, third)
A library can't do anything to extend language syntax in JavaScript. It's limited to what's available... no operator overloading or anything.

Incremental counter Javascript/JQuery

I'm trying to work out how to make this counter work. After a certain amount of animations have played, I want to make the page refresh. I know my code sucks. I'm not educated in this and I'm very new, not to mention it being difficult to concentrate (to say the least) where I'm staying at the moment... so be nice. Here it is:
$(document).ready(function () {
function loop() {
var p = 0;
if (p = 3) {
location.reload(true);
} else {
$("#p3").delay("1000").fadeIn("slow");
$("#p3").delay("1000").fadeOut("slow", loop);
p + 1;
};
loop();
});
Your if (p = 3) statement is using the assignment operator = instead of the comparison operator === or ==. So p gets assigned to 3, the result of which is truthy, so the else statement is never executed.
Also your p variable is declared inside your loop() function, so it gets reset every time the function is called - you could move that declaration to just before the function (keep it inside the document ready handler: no need to make it global).
Also the line p + 1; doesn't do anything: it doesn't increment p because you'd need to assign the result back to p with p = p + 1, the shorthand for which is p += 1 or just p++.
Finally, your code as posted has a syntax error: you are missing the closing } from the loop() function. I would guess the intention is to end the function and then call it, so:
$(document).ready(function () {
var p = 0; // <--- moved outside function
function loop() {
if (p === 3) { // <-- changed = to ===
location.reload(true);
} else {
$("#p3").delay("1000").fadeIn("slow");
$("#p3").delay("1000").fadeOut("slow", loop);
p++; // <-- changed from p + 1
};
} // <--- this is the missing bracket
loop();
});
I've made some assumptions and written what I believe is what you want, code is un-tested.:
Change it to:
$(document).ready(function () {
var globalP = 0;
//this is called when fadeOut completes.
function fadeComplete() {
if (globalP == 3) {//if it is 3 reload..
location.reload(true);
} else {
globalP++;//increment counter
animate();//start animation again...
}
}
function animate() {
//start fading in...
$("#p3").delay("1000").fadeIn("slow", function() {
//start fading out when the fadeIn completes.
//should this happen? Since you're fading in the SAME element.
$("#p3").delay("1000").fadeOut("slow", fadeComplete);
});
};
animate();
});

How to call the javascript function dynamically

I need to call the javascript function dynamically after some delay, The function display_1, 2, ... n will be dynamically constructed. My script looks like this, but the function never gets triggered if I use the following code, but if I hardcode the function it just seems to be fine.
function display_1() {
alert(1);
}
function display_2() {
alert(2);
}
function display() {
var prefix = 'display_';
for(var i = 1; i < 3; i++) {
setTimeout(prefix.concat(i), 1000);
}
window.onload = display();
Instead of going via a string, you may as well group the functions into an array:
function display_1() {...}
function display_2() { ... }
var functions = [ display_1, display_2 ];
function display() {
for( var i = 0; i != functions.length; ++i ) {
setTimeout( functions[i], 1000 );
}
}
If you want to go further, you may even leave out the explicit function names:
var functions = [
function() { /*the function_1 implementation*/
},
function() { /*the function_2 implementation*/
}
];
you have to add the parenthesis so that the function is called:
setTimeout(prefix.concat(i)+"()", 1000);
or simply:
setTimeout(prefix + i + "()", 1000);
Besides of that please note that both functions are called pratically at the same time, because the timers started with ´setTimeout()` start at the same time.
Depending on what you're trying to do you might have a look at setInterval() or start the second timeout at the end of the display_1() function.
It should be
function display_1() {
alert(1);
}
function display_2() {
alert(2);
}
function display() {
var prefix = 'display_';
for(var i = 1; i < 3; i++) {
setTimeout(prefix.concat(i)+'()', 1000);
}
}
window.onload = display;
the string passed to setTimeout should call the function
onload should be set to a function, not its return value
setInterval('load_testimonial()',5000);//first parameter is your function or what ever the code u want to execute, and second is time in millisecond..
this will help you to execute your function for every given time.
If you really want a 1000ms delay between executing the functions, you could do something like this:
window.onload = function() {
var n = 0;
var functions = [
function() {
alert(1);
setTimeout(functions[n++], 1000);
},
function() {
alert(2);
setTimeout(functions[n++], 1000);
},
function() {
alert(3);
}
];
setTimeout(functions[n++], 1000);
};
(rewrite it in a less-repetitive nature if needed)

Categories