Knowing when all other ready callbacks are done - javascript

I want my handler of the ready event will fire after all other handlers are done.
It's extremely handy for manipulating plugins' undesired actions.
If I write my handler after all others, it only guarantees it will fire after all others fired, not finished:
$(function() {
setTimeout(function() { alert('other handler'); }, 500);
});
$(function() { alert('my handler'); });​
Fiddle
In that code, my handler alerted first.
I read that before jQuery version 1.4 the readyList was public. so in version 1.7 I have no idea how I can tell that my handler is the last handler or not.

If the idea is that you don't control the other ready handlers, then given your example where another handler used a setTimeout, you can never actually know (without inspecting the other code) if your code will run after all other code.
The readyList wouldn't help even if it was public, because in your example, the handler with the setTimeout will be removed from the readyList long before the setTimeout handler runs. The readyList Array doesn't have any control over that sort of asynchronous code either.
So if you don't control (can't modify) the other code, then I really don't have a solution. But if the other code is just long running, but not asynchronous, then there wouldn't be any issue, because if your code is the last .ready() handler assigned, it shouldn't matter how long the other handlers take to execute. If their code is synchronous, it will force yours to wait until they're complete. It's just that if they're using asynchronous code, like your setTimeout example, then there's nothing you can do short of examining the other code, and modifying yours to make sure it fires last.

You can use something like this:
function Join(cb) {
var paths = 0;
var triggerCallback = cb;
this.add = function () {
paths ++;
return this.call;
};
this.call = function () {
paths --;
if (paths == 0)
if (triggerCallback)
triggerCallback();
};
return this;
}
An example:
function finishedAll() {
alert("All finished");
}
window.join = new Join(finishedAll);
function sampleCall(callJoinHandle) {
alert("Not done yet.");
if (callJoinHandle) callJoinHandle();
}
var cb1 = join.add();
setTimeout(function () { sampleCall(cb1); }, 1000);
var cb2 = join.add();
setTimeout(function () { sampleCall(cb2); }, 1000);
var cb3 = join.add();
setTimeout(function () { sampleCall(cb3); }, 1000);

An idea could be creating an array of deferred to use inside every ready function (except the last one), resolving each one when the snippet has completed.
Then, in the last ready function you could simply check the promise resolution with $.when and then execute some other code: e.g.
var dfdArray = [];
$(function() {
var dfd = $.Deferred();
dfdArray.push(dfd);
setTimeout(function() {
console.log('another simple handler');
dfd.resolve();
}, 2000);
});
$(function() {
var dfd = $.Deferred();
dfdArray.push(dfd);
setTimeout(function() {
console.log('first handler');
dfd.resolve();
}, 1200);
});
$(function() {
$.when.apply($, dfdArray).done(function() {
alert('my final handler');
})
});
See fiddle in action here: http://jsfiddle.net/DXaw5/

I don't know if it is possible for you to create a queue for all the functions like
var queue = [];
queue .push(fun1);
queue .push(fun2);
//execute the first function and remove it.
(queue .shift())();

I usually use the following pattern, simply keepig a counter of finished async functions:
var fired = 10;
var finished = 0;
for (var i = 0; i < fired; i++) {
// Call an asynchronous function 10 times
async_function(function() {
// When asynchronous function finishes,
// we check if it was the last one.
if (++finished == fired) all_ready();
});
}
The same in coffeescript:
fired = 10
finished = 0
(async_function -> all_ready() if ++finished == ready) for n in [0...fired]
(We call the same function for 10 times to keep the example simple, while in reality you may of course call different functions, but the same idea apply; in callback function you check the counter.)

Related

Run JavaScript immediately but wait x seconds before it can be run again?

Im listening for an event and I need to run a function (in this example console log for demoing my code) when it happens.
This is working however the event happens multiple times in quick succession and I only want the function to run once. How can I run the function straight away but then wait a second before its able to be triggered again?
$(document).on('someEvent', function(event, data) {
if (var === 'something') {
console.log('Run');
}
});
Update: To be clear, I need to wait for the event 'someEvent' to occur before my console function runs.
Some like that?
var is_blocked = false;
var block = function( time_to_wait ) {
is_blocked = true;
setTimeout( function() {
is_blocked = false;
}, time_to_wait );
};
$(document).on('someEvent', function(event, data) {
if ( is_blocked === false ) {
block( 1000 );
console.log('Run');
}
});
If you don't mind using an external library, use lodash's debounce method. Note that the sample in the docs is pretty similar to the case you described. The options (leading/trailing) can be used to customize the behavior.
There's a tiny library called underscore.js that has a ton of useful functions. Among these there is _.debounce:
debounce_.debounce(function, wait, [immediate])
Creates and returns a new debounced version of the passed function
which will postpone its execution until after wait milliseconds have
elapsed since the last time it was invoked. Useful for implementing
behavior that should only happen after the input has stopped arriving.
For example: rendering a preview of a Markdown comment, recalculating
a layout after the window has stopped being resized, and so on.
Pass true for the immediate argument to cause debounce to trigger the
function on the leading instead of the trailing edge of the wait
interval. Useful in circumstances like preventing accidental
double-clicks on a "submit" button from firing a second time.
In your case it's a matter of wrapping the handler function like this (I used 100ms for the timeout):
$(document).on('someEvent', _.debounce(function(event, data) {
if (var === 'something') {
console.log('Run');
}
}, 100));
Function "functionToBeCalled()" will be executed immediately, and every 0.4 seconds. If you want to call again that function after 0.4s and not every time replace setInterval with setTimeout.
var variable = "something";
$("#button").on('click', function(event, data) {
if ( variable === 'something') {
console.log('Run');
setTimeout(function(){
$("#button").trigger("click");
}, 1000)
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="button">Button</div>
You could use the current time:
var waitUntil = Date.now();
$(document).on('someEvent', function(event, data) {
if (Date.now() >= waitUntil) {
waitUntil = Date.now() + 5000 // 5 seconds wait from now
console.log('Run');
}
});
Here is a fiddle which uses a button click as the event, and gives feed-back on-screen about whether the event is treated or not.
Here's a neat little function that might help:
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
}

jQuery: Making a Plugin Wait Until Existing Calls Are Done To Execute?

I should begin by saying that, while I'm pretty familiar with basic JavaScript/jQuery, I'm by no means an expert. So this could be a simple concept I'm not grasping. At any rate, I'll explain my situation as best I can.
I have been piecing together a jQuery plugin that takes a string and displays it one character at a time until the end of the string has been reached. Each call starts and stops on an interval as needed until done:
$.fn.writeText = function(content) {
var elem = this;
var contentArray = content.split("");
return this.each(function() {
var current = 0;
var length = contentArray.length;
intervalText();
function intervalText(){
var interval = setInterval(function(){
if(current < length){
//If end punctuation is detected [omitted for simplicity's sake]
{
elem.text(elem.text() + contentArray[current++],play('tes.wav'));
clearInterval(interval);
setTimeout(function(){intervalText();},500);
}
//Else if 'pause' punctuation is detected.
{
elem.text(elem.text() + contentArray[current++],play('tes.wav'));
clearInterval(interval);
setTimeout(function (){intervalText();},200);
}
else{
elem.text(elem.text() + contentArray[current++],play('tes.wav'));
}
}
else if(current == length+1){
clearInterval(interval);
}
}, 50);
}
});
}
One thing I've noticed is that using the plugin back to back causes problems:
$("#promptText").writeText("This test.");
$("#promptText").writeText("This is also a test.");
//Results in "TThhiiss tiess ta.lso a test."
I determined pretty quickly that the issue is just that characters are being added to the same element, and that the plugin isn't doing anything "wrong." But, as you can imagine I'd like to prevent this from happening.
In order to do that, I'd need to cause any secondary calls to the plugin to wait until the previous ones were finished. Any suggestions as to how I'd go about this?
This is a great application for jQuery's Deferred() objects. Deferreds let you start a function, then chain additional calls after that function completes (the $.ajax() function uses Deferreds internally).
In this case, you can queue up a new Deferred each time you call $().writeText(), and make them call your internal function in order:
var deferred;
$.fn.writeText = function (content) {
var d = $.Deferred(); //Each execution gets its own deferred
var elem = this;
var f = function () {
writeTextInternal(content, elem, d); //Do it
}
if (deferred) { //If there is already a deferred, queue the new request
deferred.then(f); //Calls f() when deferred is resolved, or immediately if already resolved
} else {
f(); //Call immediately, because there are no deferreds
}
deferred = d.promise(); //Save this deferred to chain subsequent calls.
return deferred; //Allows other functions to do something after this text is typed.
}
var writeTextInternal = function(content, elem, d) {
//Slightly modified original function that will resolve our deferred...
}
Working JSFiddle: http://jsfiddle.net/bt5tee3r/3/

Stopping .each execution on every iteration for a spesific time interval in jquery?

I am trying to create the following functionality in my javascript:
$("mySelector").each(function(){
// Do something (e.g. change div class attribute)
// call to MyFunction(), the iteration will stop here as long as it will take for myFunction to complete
});
function myFunction()
{
// Do something for e.g. 5 seconds
}
My question is how can I stop every iteration for the duration of the myFunction()?
No, that isnt possible. You'll have to code it differently, possibly with a setTimeout based on the current index of .each.
$("mySelector").each(function(i){
// Do something (e.g. change div class attribute)
// call to MyFunction(), the iteration will stop here as long as it will take for myFunction to complete
setTimeout(myFunction,i*5000);
});
function myFunction()
{
// Do something for e.g. 5 seconds
}
Edit: You can also do it with queuing: http://jsfiddle.net/9Bm9p/6/
$(document).ready(function () {
var divs = $(".test");
var queue = $("<div />");
divs.each(function(){
var _this = this;
queue.queue(function(next) {
myFunction.call(_this,next);
});
});
});
function myFunction(next) {
// do stuff
$(this).doSomething();
// simulate asynchronous event
var self = this;
setTimeout(function(){
console.log(self.id);
// go to next item in the queue
next();
},2000);
}
​
Here's a jsFiddle that I think will do what you need:
http://jsfiddle.net/9Bm9p/2/
You would just need to replace the selector with what you use.
The "loop" that is occurring will wait for myFunction to finish before moving on to the next element. I added the setTimeout inside of myFunction to simulate it taking a period of time. If you are using asynchronous things, such as an AJAX request, you would need to put the call to myFunction inside of the complete method...or in the callback of an animation.
But as someone already commented, if everything in myFunction is synchronous, you should be able to use it as you are. If you are looking for this process to be asynchronous, or if things in myFunction are asynchronous, you cannot use a for loop or .each().
(function () {
"use strict";
var step = 0;
var content = $("mySelector");
var max = content.length;
var speed = 5000; // ms
var handle = setInterval(function () {
step++;
if (step >= max) {
clearInterval(handle);
} else {
var item = content[step];
// do something
}
}, speed);
}());
setInterval will do it once-every-n-miliseconds, and clearInterval will stop it when you're done. This won't lock up the browser (provided your "do something" also doesn't). FRAGILE: it assumes that the results of $("mySelector") are valid for the duration of the task. If that isn't the case then inside do something then validate item again.

Wait till a Function with animations is finished until running another Function

I'm having an issue with normal (non-ajax) functions that involve lots of animations within each of them. Currently I simply have a setTimeout between functions, but this isn't perfect since no browsers / computers are the same.
Additional Note: They both have separate animations/etc that collide.
I can't simply put one in the callback function of another
// multiple dom animations / etc
FunctionOne();
// What I -was- doing to wait till running the next function filled
// with animations, etc
setTimeout(function () {
FunctionTwo(); // other dom animations (some triggering on previous ones)
}, 1000);
Is there anyway in js/jQuery to have:
// Pseudo-code
-do FunctionOne()
-when finished :: run -> FunctionTwo()
I know about $.when() & $.done(), but those are for AJAX...
MY UPDATED SOLUTION
jQuery has an exposed variable (that for some reason isn't listed anywhere in the jQuery docs) called $.timers, which holds the array of animations currently taking place.
function animationsTest (callback) {
// Test if ANY/ALL page animations are currently active
var testAnimationInterval = setInterval(function () {
if (! $.timers.length) { // any page animations finished
clearInterval(testAnimationInterval);
callback();
}
}, 25);
};
Basic useage:
// run some function with animations etc
functionWithAnimations();
animationsTest(function () { // <-- this will run once all the above animations are finished
// your callback (things to do after all animations are done)
runNextAnimations();
});
You can use jQuery's $.Deferred
var FunctionOne = function () {
// create a deferred object
var r = $.Deferred();
// do whatever you want (e.g. ajax/animations other asyc tasks)
setTimeout(function () {
// and call `resolve` on the deferred object, once you're done
r.resolve();
}, 2500);
// return the deferred object
return r;
};
// define FunctionTwo as needed
var FunctionTwo = function () {
console.log('FunctionTwo');
};
// call FunctionOne and use the `done` method
// with `FunctionTwo` as it's parameter
FunctionOne().done(FunctionTwo);
you could also pack multiple deferreds together:
var FunctionOne = function () {
var
a = $.Deferred(),
b = $.Deferred();
// some fake asyc task
setTimeout(function () {
console.log('a done');
a.resolve();
}, Math.random() * 4000);
// some other fake asyc task
setTimeout(function () {
console.log('b done');
b.resolve();
}, Math.random() * 4000);
return $.Deferred(function (def) {
$.when(a, b).done(function () {
def.resolve();
});
});
};
http://jsfiddle.net/p22dK/
add the following to the end of the first function
return $.Deferred().resolve();
call both functions like so
functionOne().done(functionTwo);
Along with Yoshi's answer, I have found another very simple (callback type) solution for animations.
jQuery has an exposed variable (that for some reason isn't listed anywhere in the jQuery docs) called $.timers, which holds the array of animations currently taking place.
function animationsTest (callback) {
// Test if ANY/ALL page animations are currently active
var testAnimationInterval = setInterval(function () {
if (! $.timers.length) { // any page animations finished
clearInterval(testAnimationInterval);
callback();
}
}, 25);
};
Basic useage:
functionOne(); // one with animations
animationsTest(functionTwo);
Hope this helps some people out!
This answer uses promises, a JavaScript feature of the ECMAScript 6 standard. If your target platform does not support promises, polyfill it with PromiseJs.
You can get the Deferred object jQuery creates for the animation using .promise() on the animation call. Wrapping these Deferreds into ES6 Promises results in much cleaner code than using timers.
You can also use Deferreds directly, but this is generally discouraged because they do not follow the Promises/A+ specification.
The resulting code would look like this:
var p1 = Promise.resolve($('#Content').animate({ opacity: 0.5 }, { duration: 500, queue: false }).promise());
var p2 = Promise.resolve($('#Content').animate({ marginLeft: "-100px" }, { duration: 2000, queue: false }).promise());
Promise.all([p1, p2]).then(function () {
return $('#Content').animate({ width: 0 }, { duration: 500, queue: false }).promise();
});
Note that the function in Promise.all() returns the promise. This is where magic happens. If in a then call a promise is returned, the next then call will wait for that promise to be resolved before executing.
jQuery uses an animation queue for each element. So animations on the same element are executed synchronously. In this case you wouldn't have to use promises at all!
I have disabled the jQuery animation queue to demonstrate how it would work with promises.
Promise.all() takes an array of promises and creates a new Promise that finishes after all promises in the array finished.
Promise.race() also takes an array of promises, but finishes as soon as the first Promise finished.
Is this what you mean man: http://jsfiddle.net/LF75a/
You will have one function fire the next function and so on, i.e. add another function call and then add your functionONe at the bottom of it.
Please lemme know if I missed anything, hope it fits the cause :)
or this: Call a function after previous function is complete
Code:
function hulk()
{
// do some stuff...
}
function simpsons()
{
// do some stuff...
hulk();
}
function thor()
{
// do some stuff...
simpsons();
}
ECMAScript 6 UPDATE
This uses a new feature of JavaScript called Promises
functionOne().then(functionTwo);
You can do it via callback function.
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable, function() {
function2(someOtherVariable);
});
}
else {
doThis(someVariable);
}
});
function function1(param, callback) {
...do stuff
callback();
}
Here is a solution for n-calls (recursive function).
https://jsfiddle.net/mathew11/5f3mu0f4/7/
function myFunction(array){
var r = $.Deferred();
if(array.length == 0){
r.resolve();
return r;
}
var element = array.shift();
// async task
timer = setTimeout(function(){
$("a").text($("a").text()+ " " + element);
var resolving = function(){
r.resolve();
}
myFunction(array).done(resolving);
}, 500);
return r;
}
//Starting the function
var myArray = ["Hi", "that's", "just", "a", "test"];
var alerting = function (){window.alert("finished!")};
myFunction(myArray).done(alerting);
You can use the javascript Promise and async/await to implement a synchronized call of the functions.
Suppose you want to execute n number of functions in a synchronized manner that are stored in an array, here is my solution for that.
async function executeActionQueue(funArray) {
var length = funArray.length;
for(var i = 0; i < length; i++) {
await executeFun(funArray[i]);
}
};
function executeFun(fun) {
return new Promise((resolve, reject) => {
// Execute required function here
fun()
.then((data) => {
// do required with data
resolve(true);
})
.catch((error) => {
// handle error
resolve(true);
});
})
};
executeActionQueue(funArray);

How to call an event handler dynamically and get its return value in javascript?

I asked a question yesterday, I accepted the answer, but sometime later, I came to know that the solution was not complete. The question is :-
Insert a JQuery click handler that executes before the ones already registered
using setTimeout(handler,0); returns immediately and I can not use return setTimeout(handler,0);. How can I run this handler synchronously and not allow the parent function to complete until this handler is completely executed and I get the return value out of it ?
I am in hurry, so I am asking it again, rather than editing that question again.
You don't need to use setTimeout. If u don't use setTimeout, your handler reamins synchronous, and you can return all the value u want in your function.
<script>
function f2() {
alert('Handler declared in HTML')
}
buttons = document.getElementsByTagName('input'); // refine this, later
for (i = 0, max = buttons.length; i < max; i++) {
oldonclick = buttons[i].onclick;
buttons[i].onclick = function() {
alert('Prepend handler');
oldonclick();
}
}
</script>
Since timeouts are asynchronous you’ll need to set the variable from within the timeout (and/or call a callback function).
var x = 1;
setTimeout(function() {
x = 2;
}, 2000);
Here’s an example with a callback function. You need this is you want to do something with the variable as soon as it’s changed.
var x = 1;
function callback(x) {
console.log(x);
}
setTimeout(function() {
x = 2;
callback(x);
}, 2000);
This will log 2 as soon as the timeout is executed.
Depending on what exactly it is you’re trying to do, you may not need timeouts at all, which avoids asynchronicity and a lot of trouble.
Quick answar: What about changing:
setTimeout(clickhandler, 0);
to
eval(clickhandler)();
to
eval(clickhandler);
$(document).ready(function() {
$("input[type=button]").each(function() {
// your button
var btn = $(this);
// original click handler
var clickhandler = btn.data("events").click[0];
btn.unbind("click", clickhandler);
// new click handler
btn.click(function() {
alert('Prepended Handler');
clickhandler();
});
});
});
function f2() {
alert('Handler declared in HTML');
}
And now clickhandler is a function, right?
See: jQuery: Unbind event handlers to bind them again later

Categories