running code when two events have triggered - javascript

This is mostly a language-agnostic question.
If I'm waiting for two events to complete (say, two IO events or http requests), what is the best pattern to deal with this. One thing I can think of is the following (pseudo js example).
request1.onComplete = function() {
req1Completed = true;
eventsCompleted();
}
request2.onComplete = function() {
req2Completed = true;
eventsCompleted();
}
eventsCompleted = function() {
if (!req1Completed || !req2Completed) return;
// do stuff
}
Is this the most effective pattern, or are there more elegant ways to solve this issue?

Before even going into the details, here's something neat that takes advantage of lambda functions off the top of my head:
function makeCountdownCallback(count, callback) {
return function() {
if (--count == 0)
callback();
};
}
request1.onComplete = request2.onComplete = makeCountdownCallback(2, function() {
// do stuff
});
This obviously assumes that each event fires at most once, and doesn't take advantage of order.

jQuery 1.5 has Deferreds: http://api.jquery.com/category/deferred-object/
You can easily set them up to call back only when some events have been triggered.

Try #1: Here's a solution that doesn't require additional global variables:
request1.onComplete = function() {
// register new handler for event2 here, overwriting the old one
request2.onComplete = function() {
// now they're both done
}
}
request2.onComplete = function() {
// register new handler for event1 here, overwriting the old one
request1.onComplete = function() {
// now they're both done
}
}
The handler for whichever event fires first will clear the other's old handler and assign a new one that includes the stuff you need to do after the completion of both events. Because we re-assign the second handler inside the handler of the first event (whichever that is), we always know we're done when that second handler finishes.
Try #2: Here's something that will work if each event type is different:
function onBoth(fn) {
var last, done = false;
return function(e) {
if (last && last !== e.type && !done) {
fn(); // done
done = true;
}
last = e.type;
}
}
For example, this won't alert "done" until the user has both scrolled and clicked:
var both = onBoth(function() {
alert("done")
});
document.addEventListener("scroll", both, false);
document.addEventListener("click", both, false);
Try #3: The previous try can be modified to work for similar events:
function onBoth(fn) {
var last, done = false;
return function(curr) {
if (last && last !== curr && !done) {
fn(); // done
done = true;
}
last = curr;
}
}
...which should be used like this:
var check = onBoth(function() {
alert("done")
});
request1.onComplete = function() {
check(arguments.callee);
}
request2.onComplete = function() {
check(arguments.callee);
}
Basically, this checks that two different callbacks have executed by storing a reference to the most recently executed call back. Its usage is a little clunky, but it gets the job done (i.e. it will still work if each of the events executes more than once).

One way to do it: http://tobireif.com/posts/waiting_for_two_events/
Q.spread([
getA(), getB()
], function(a, b) {
// Use the results a and b.
// Catch errors:
}).catch(function(error) {
// Minimal for this example:
console.log(error);
});
The lib is at https://github.com/kriskowal/q .

Related

JS how to do something only once

For example:
// run this:
alert('Loading...');
// dont run this again:
alert('Loading...');
I don't want to ever repeat that.
How can I do this with convenience (preferably without using booleans)?
The standard way is to use a boolean flag.
But, if you have an aversion to booleans, you can do it by overwriting the function, thus ensuring it literally can never be called again.
function loadButOnlyOnce() {
console.log('This will only happen once!');
loadButOnlyOnce = function() {};
}
loadButOnlyOnce();
loadButOnlyOnce();
Your IDE will probably spew out warnings along the lines of "What are you doing, this is overwriting the function!". But it's your code, and you can do it if you want.
So, now you want a generic solution you can use with different functions? You can do this like so:
function allowOnlyOneCall(f) {
return function() {
f.apply(this, arguments);
f = function() {};
}
}
function myMethod(p1) {
console.log('myMethod is being invoked. p1 = ' + p1);
}
myMethod = allowOnlyOneCall(myMethod);
myMethod(5);
myMethod(5);
Here is one clean implementation you could use so you can avoid the usage of booleans for every single task you don't want to repeat:
var cache = [];
function do_once(task, fn) {
if(cache.indexOf(task) == -1) { // shorthand: !~cache.indexOf(task)
cache.push(task);
return fn();
}
}
Usage:
var alertLoading = alert.bind(null, "Loading...");
do_once("alert_loading", alertLoading); // will run
// later on...
do_once("alert_loading", alertLoading); // will not run again
This works as long as you give each task you don’t want to repeat a different name. Regardless of the function provided as the second argument to do_once, it will not run as long as do_once has already been called using the same task name.
First create a variable to store whether the function has already run or not (such as hasRun). Then simply store your functionality inside an if condition which checks that this variable is false. You would need to update this variable after running the logic for the first time.
This can be seen in the following:
hasRun = false;
document.getElementsByTagName('button')[0].addEventListener('click', function() {
if (!hasRun) {
console.log('Loading...'); // Gets run once
hasRun = true; // Set the flag to true so the conditional doesn't get entered again
} else {
console.log('The function already ran!'); // Runs every subsequent time
}
})
<button>Click me</button>
If you want a factory function that memoizes the result of calling a function with a single primitive value as a parameter, you should use a Map:
const once = (() => fn => {
const cache = new Map()
return value => {
if (!cache.has(value)) {
cache.set(value, fn(value))
}
return cache.get(value)
}
})()
function verboseSquare (value) {
console.log('expensive calculation')
return value * value
}
const squareOnce = once(verboseSquare)
console.log(squareOnce(4))
console.log(squareOnce(4)) // skipped work
console.log(squareOnce(5))
console.log(squareOnce(5)) // skipped work

JavaScript Singleton losing reference

I have a problem with a singleton pattern that I have implemented in 'program.js':
var Program = (function() {
var _program; // Instance of program
// Constructor
function Program() {
if (typeof _program != "undefined") {
throw new Error("Program can only be instantiated once.");
}
this.run = false; // flag to allow for program exec
_program = this;
};
// Public methods
Program.prototype.init = function() {
// the run call is nested in 4 callbacks
callbackHell (
this.run = true;
);
};
Program.prototype.execute = function() {
if(this.run == true) {
// do stuff
}
};
Program.getProgram = function () {
if(typeof _program == "undefined")
{
return new this();
}
return _program;
};
// Return the constructor
return Program;
})();
In my 'main.js' I keep a reference to the loaded program and check for the run flag to allow execution. Looks like this:
var program = null;
function initDemo() {
program = Program.getProgram();
program.init();
runDemo();
};
function runDemo() {
if(program != null && program.run) {
program.execute();
}
requestAnimationFrame(runDemo);
};
If I execute this code on a Chrome browser, it will never reach the program.execute() call in main.js. The reference of program will keep the run flag to false, even it is changed in the init() function. I checked for all of this in the debugger. I should point out that the 'this.run = true' call is nested in 4 callbacks. After a while I figured I could just change the run flag of the global reference of program in main.js with the init() function. So instead of 'this.run = true', 'program.run = true'. This works and the loop will run execute(). However this is not the style I am used from OOP. What is actually happening here? It definitely has to do with the callbacks: when I put 'this.run = true' out of the callbacks at the end of init() the flag is changed correctly, however at the wrong time of the program execution.
Probably your callbacks in the callbackHell are doing something asynchronously and there is a delay before program.run will actually be set to true, the sequence is approximately this:
You call program.init()
Your callbacks start working
You call runDemo(), here program.run is false and it exists
Callbacks finish their work and program.run becomes true
The solution to that is to make your runDemo to be another callback, so your main code will look like this:
var program = null;
function initDemo() {
program = Program.getProgram();
program.init(runDemo);
};
function runDemo() {
if(program != null) {
program.execute();
}
requestAnimationFrame(runDemo);
};
Here you don't need the program.run flag at all, instead you just start your demo from inside the "callbackHell":
Program.prototype.init = function(functionToRun) {
// the run call is nested in 4 callbacks
callbackHell (
functionToRun(); // instead of setting program.run
// we call the specified function
);
};

Creating an async method in javascript

I'm writing javascript code to update the interface from the user.
This task has to be done in the background and shouldn't block anything.
The code is currently blocking/crashing the browser, the while loop is causing the issue.
What i would like is to wait till the checking and installing are complete and then perform other actions also in between. I want to avoid to write the scenario: TimeOut in a TimeOut in a TimeOut, which does work but it makes the code a mess.
updateChecking();
function updateChecking() {
setTimeout(function() {
if (settings.IsChecking === "True") {
var isChecking = false;
var isInstalling = false;
// PROBLEM, Wait till checking is completed
while (isChecking) {
var timerUpdateChecker = setInterval(function() {
getIsCheckingUpdates().done(function(resultIsChecking) {
if (resultIsChecking === "False") {
isChecking = true;
clearInterval(timerUpdateChecker);
}
});
}, 1000);
checkForSystemUpdates();
startCheckingDownloads();
// PROBLEM, Wait till installing is completed
while (isInstalling) {
setInstallerInterface();
var timerInstallChecker = setInterval(function() {
getIsInstallingUpdates().done(function(resultIsUpdating) {
if (resultIsUpdating === "False") {
isInstalling = true;
clearInterval(timerInstallChecker);
}
});
}, 1000);
}
viewUpdateInstalledAlert();
getAvailableUpdates();
unsetInstallerInterface();
};
}
}, 0);
}
Any suggestions that could solve my issue?
While loops run until the condition if false. Now you're setting the variable to false and call while. So I'm not sure how the while will work. From what I understand it should be while(!isChecking).
That said, you should maybe try to replace your setTimeout and setInterval and replace with events. You can create custom event, dispatch and listen. I think it would be much more efficient. Something like this:
var event = new Event('isInstalled');
In your installed function you dispatch the event:
window.dispatchEvent(event);
And you listen to the event to trigger wanted functions.
window.addEventListener('isInstalled', function(){
//whatever needs to happen when installed is done
})

Calling a function recursively with setTimeout

I want call few function one after another recursively with setTimeout.
var flag = 0 ;
function slave1(){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT1"); // Checking if DOM has loaded or not. If yes then doing something.
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
} else {
setTimeout(slave1,2000); //waiting for 2 seconds and checking again.
}
}
}
//doing similar task
function slave2(){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT2");
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
} else {
setTimeout(slave2,2000);
}
}
}
function master() {
slave1();
console.log("Without completing slave1 function.");
slave2();
}
Through master() function I want to call multiple functions one after another, however in current situation its calling slave2() without completing slave1(). How can I make sure that slave1() has executed completed. If DOM element is not loaded than it should execute 60 times after every 2 seconds and than it should come out from slave1() and go to next one.
I want to execute same function for 60 times if dom element is not loaded without returning the control to next function.
You need to adjust slave1 to run a callback when it is finished which will be slave2.
function slave1(callback){
if(flag < 60) {
var COPY_PO_LINE_DIV = document.getElementById("DOM_ELEMENT1"); // Checking if DOM has loaded or not. If yes then doing something.
if (COPY_PO_LINE_DIV != null) {
flag = 0;
//doing something
callback();
} else {
setTimeout(slave1,2000); //waiting for 2 seconds and checking again.
}
}
}
function slave2(){...}
function master() {
slave1(slave2);
console.log("Without completing slave1 function.");
}
This is your basic javascript chaining. If you have more slaves you might want to look into async.series otherwise you go into callback hell as Gabs00 has put it nicely:
slave1(function(){
slave2(function(){
slave3(function(){
slave4(slave5);
});
});
});
If you need to pass values to callbacks then you need to use an intermediate anonymous function which in turn calls the intended callback with the arguments in question. To do that, you need define your functions so that they use the arguments:
function slave1(str, callback){...}
function slave3(i, callback){...}
slave1("some argument", function(){
slave2("another argument", function(){
slave3(1, function(){
slave4(2, slave5);
});
});
});
Consider using promises for things like that. Here an implementation on top of jQuery, other promise libraries work similarly.
function waitForElement(elementId, maxTries, checkInterval) {
var d = $.Deferred(), intvalID, checkFunc;
// set up default values
maxTries = maxTries || 60;
checkInterval = checkInterval || 2000;
checkFunc = function () {
var elem = document.getElementById(elementId);
if (maxTries-- > 0 && elem) {
clearInterval(intvalID);
d.resolve(elem);
}
if (maxTries <= 0) {
clearInterval(intvalID);
d.reject(elementId);
}
};
// set up periodic check & do first check right-away
intvalID = setInterval(checkFunc, checkInterval);
checkFunc();
return d.promise();
}
Now, if you want to test for elements one after another, you can cascade the calls like this:
function master() {
waitForElement("DOM_ELEMENT1").done(function (elem1) {
waitForElement("DOM_ELEMENT2").done(function (elem2) {
alert("elem1 and elem2 exist!");
// now do something with elem1 and elem2
}).fail(function () {
alert("elem1 exists, but elem2 was not found.");
});
}).fail(function () {
alert("elem1 not found.");
});
}
or you can do it in parallel and have a callback called when all of the elements exist:
function master() {
$.when(
waitForElement("DOM_ELEMENT1"),
waitForElement("DOM_ELEMENT2")
)
.done(function (elem1, elem2) {
alert("elem1 and elem2 exist!");
// now do something with elem1 and elem2
})
.fail(function () {
alert("not all elements were found before the timeout");
});
}
Your slave2 function should be passed to slave1 function as a callback and should be called in slave1 after it finishes (if ever?). Your current situation is quite common, since setTimeout() function is asynchronous, thus JS interpreter doesn't wait till the function is completed, but sets the setTimeout() result at the end of the Evet Loop and continues processing the master() method.
In order to pass arguments to functions, creating anonymous functions turns out to be an overkill. Consider using "bind" instead. So, if you've got
function slave1(str, callback){...}
function slave2(str, callback){...}
function slave3(i, callback){...}
function slave4(i, callback){...}
function slave5()
Instead of using
slave1("some argument", function(){
slave2("another argument", function(){
slave3(1, function(){
slave4(2, slave5);
});
});
});
Consider using
slave1("some argument",
slave2.bind(null, "another argument",
slave3.bind(null, 1,
slave4.bind(null, 2, slave5)
)
)
);
Much easier, more efficient in terms of memory and CPU utilization.
Now, how to do this with setTimeout:
slave1("some argument",
setTimeout.bind(null, slave2.bind(null, "another argument",
setTimeout.bind(null, slave3.bind(null, 1,
setTimeout.bind(null, slave4.bind(null, 2,
setTimeout.bind(null, slave5, 0)
),0)
),0)
),0)
);
I explained the problem in more detail at
http://morethanslightly.com/index.php/2014/09/executables-the-standard-solution-aka-mind-the-bind/

How to detect when one function is complete from another function?

I have a javascript function that is being built to animate the collapse of a div, and then proceed with other jobs. The code is as follows:
function newsFeed() {
var self = this;
this.collapse = function(listingID,orig_height,curr_height,opacity) {
var listing = document.getElementById(listingID);
var reduceBy = 5;
if(curr_height > reduceBy) {
curr_height = curr_height-reduceBy;
listing.style.overflow = "hidden";
listing.style.height = (curr_height-40) + "px";
if(opacity > 0) {
opacity = opacity - 10;
var opaque = (opacity / 100);
listing.style.opacity=opaque;
listing.style.MozOpacity=opaque;
listing.style.filter='alpha(opacity='+opacity+')';
}
setTimeout(function() { self.collapse(listingID,orig_height,curr_height,opacity); },1);
}else{
return true;
}
}
this.remove = function(listingID) {
var listing = document.getElementById(listingID);
var currHeight = listing.offsetHeight;
if (this.collapse(listingID,currHeight,currHeight,100)) {
// DO SOME OTHER STUFF
}
}
}
var newsFeed = new newsFeed();
newsFeed.remove('closeMe');
I cannot get the this.remove function to wait while this.collapse finishes and returns true. Is this impossible? What is the best way to go on?
Important: I would like to be able to use this.collapse with other functions yet to be built in the same fashion as I do here.
I cannot get the this.remove function to wait while this.collapse finishes
That is correct, it is impossible to do so. In JavaScript there is a single flow of execution. When the browser calls your code you can do some processing, but for anything further to occur (timeouts or event calls) you must return control to the browser.
‘Asynchronous’ processes like collapse() are done by setting timeouts, so control must be returned to the browser many times; when remove() calls collapse() the first time it returns immediately after the first timeout is set; that timeout cannot be fired until remove() itself returns, so your 'if' code will only ever execute if the very first call to collapse() was the last frame of animation (ie. the element was 5px or smaller already). Otherwise collapse()'s ‘return true’ will just be returning true to the browser's timeout-caller, which doesn't care at all what value you return to it.
Some languages give you tools such as threads or coroutines that can allow an asynchronous routine to be run from a synchronous routine; JavaScript does not. Instead, remove() must supply collapse() with a callback function it can call itself on the last frame.
There is no way you can pause the execution in Javascript till something else happens. All you can do is attach a callback function to collapse to call after it is done executing the final step.
As a sidenote, jQuery provides functions like fade(), animate() etc and supports queuing. If you don't want to use jQuery, you can still look at the code to see how it's implemented.
See the examples in this page.
setTimeout is not a "sleep". The function will end right there and return "undefined".
To manage that, I think you should do something like:
var newsFeed = new newsFeed();
newsFeed.onaftercollapse = function () {
newsFeed.remove('closeMe'); // "newsFeed" or "self"? must test
};
And then instead of return true;, the collapse() will end with:
if (self.onaftercollapse) self.onaftercollapse();
This example demonstrates how to check if a function is complete.
function foo() {
foo.complete = false;
// your code here
foo.complete = true;
}
foo.complete = false;
if (foo.complete) { // foo execution complete
// your code here
}
This code demonstrates how to check if a function has been run once.
function foo() {
// your code here
foo.ranOnce || (foo.ranOnce = true);
}
foo.ranOnce = false;
if (foo.ranOnce) { // foo execution complete at least once
// your code here
}

Categories

Resources