I'm building a concert calendar that's very heavy on javascript (using jQuery). I'm having trouble synchronizing various events throughout the app, and I'm looking for suggestions for how to do this.
An example of a simple use case:
User clicks a month
Calendar skips to that month
An example of a more complex use case:
User selects an artist
Calendar determines the date of that artist's first show
Calendar skips to that month
Calendar highlights that artist's show(s)
One occasional problem is that the new month isn't yet rendered by the time I try to highlight the artist's show(s). Thus, the show isn't highlighted even though these functions are called in order. Obviously, using setTimeout() is pretty hacky, and not guaranteed to work.
So first, a simple question -- would it ever be possible (even in Chrome) for the following function to run out of sequence?
function steps(){
stepOne(); //TAKES 30 SECONDS
stepTwo(); //TAKES < 1 SECOND
}
Second, a related simple question:
If placed at the end of a function, will a JS callback ALWAYS run after
everything else in the given function has finished running?
If so, I could nest each function as a callback of its previous function. But that would likely become unwieldy, once you consider all the different use cases.
Here's an approach I'm considering:
1) Allow each function an optional callback parameter. If it's present,
call it at the very end of the function.
2) During a UI refresh, create an array, and stack any functions to be
called within this array.
3) Once this "script" is completed, iterate through the array, calling
each function in the order it was added, using the next function
as the previous function's callback.
I imagine this would ensure that all the functions are called in order.
Another approach is to attach event listeners using
$(document).bind("listener.name", fnCallback);
And then calling
$(document).trigger("listener.name");
Whenever that event occurs.
However, I'm guessing this would be kind of unwieldy as well, considering different events might need to call different sets of functions depending on the use case. I could always call
$(document).unbind("listener.name");
before adding new events to it, but again -- I'm leaning toward creating sort of a master "script" as I suggested in the first approach.
Hopefully this isn't too vague -- any feedback? Any experience with complex UIs that had to synchronize various events?
Thanks very much,
Michael
Your approach #1 is the best way, and the most natural using jQuery. Most functions that act on the user interface and do something accept a callback function parameter, which gets called after the function has executed.
Where you are doing things not implemented in jQuery following the same pattern will make your code more readable. dominic's answer is a good terse example:
function steps(){
stepOne(stepTwo);
}
function stepOne(callback){
var AsyncDone = function() {
//any Synchronus Things here
callback();
}
someAsyncFunction( params, AsyncDone );
}
Try passing in the function to be called back to as an argument
function steps(){
stepOne(stepTwo);
}
function stepOne(callback){
var AsyncDone = function() {
//any Synchronus Things here
callback();
}
someAsyncFunction( params, AsyncDone );
}
The last approach, of using custom events, is the best approach in my humble opinion.
Design various components and events and let them interact with each other.
E.g. Let's say you have a Calendar object.
var calendar = function()
{
var pub = {};
pub.highlightRow = function(row) {};
pub.getRowByContent = function(content) { };
pub.selectMonth = function()
{
//your code to actually select month.
//Once all the necessary DOM work is done, fire the
//monthSelected event (namespacing the event so as to avoid the clash with other events).
$(document).trigger('calendar:monthSelected');
};
return pub;
}();
Now your artist search method may look like,
function searchArtist(artistName)
{
$(document).bind('calendar:monthSelected'), function()
{
calendar.highlightRow(calendar.getRowByContent(artistName));
}
);
calendar.selectMonth(getShowMonthByArtist(artistName));
}
You can use javascript's then() method to synchronize the functions and have them execute in the order that you want, like so:
function steps() {
stepOne().then(function() { stepTwo(); })
}
Related
I'm adapting to using Protractor as a UI automation framework, having used Selenium with Java, Ruby and Groovy extensively in the past. I'm new to Javascript so I'm not sure if some of my old tricks are transferrable.
One of the most useful things I came up with in Ruby and Groovy was a "multiwait" library that would allow me to wait for one of set of mutually exclusive events to occur. Given a map in which the keys are simple descriptions of events, and the values are chunks of executable code, the function would simply loop through each of the possible events, and when an event returned true, the function would return the key for that event. If none of the events returned true within a given timeout, an Exception would be thrown summarizing the events and the time that it waited for. There was also a special event, "nothing," for situations in which an action might give rise to a warning or error event if something was wrong with the data, but would usually elicit no response at all if the data was good. If "nothing" was one of the events, then instead of throwing an Exception at the end of the timeout, the function would return the key for the "nothing" event.
I am now trying to re-implement this method in Javascript, but I'm very new to the language and I'm not sure of the best way to go about it.
So I know that in Javascript I can store functions as variables. Should I be using functions as my event values in this Hash Table? Will they present any problems with scope, or should everything work as long as each function is able to see the variables that it uses?
If someone could run through a simple example with me that'd be very helpful. Let's say that I have a page object with a method called getColor which retrieves some information from the DOM. In this case let's say that it will always return the String red. I want to create a method in a separate file that will accept a Hash Table of events, something like this:
var WaitForEvent = require('../../waitForEvent');
var wait = new WaitForEvent();
function getColor() {
return 'red';
}
var outcomes = {};
outcomes['Data accepted'] = function () { getColor() == 'green' };
outcomes['Data rejected'] = function () { getColor() == 'red' };
var result = wait.waitFor(outcomes);
expect(result).toBe('Data accepted');
So, that's a rough idea of how I want to set up the parameters for the method itself. Would functions like that work in this context? Could a waitForEvent function loop through the functions, testing each one to see which one returned true first, and then return the key describing the event that occurred? Or do I need to go about this in a different way?
Assuming it would work, what would it look like in the method itself, as I'm looping through the values of the Hash Table which are themselves nameless functions? What's the proper way to execute such a function and check what it returns?
One thing I'm already aware of in Selenium, which I'd like to avoid, is Selenium's ExpectedConditions with the OR condition chaining conditions together. I'd rather something more flexible than that.
Thanks!
Let's say you have (I'm translating these to es6):
const getColor = () => 'red';
outcomes['Data rejected'] = () => getColor() === 'red';
Now you can do:
let keys = Object.keys(outcomes).filter(k => outcomes[k]())
and keys[0] will be 'Data rejected' (instead of undefined)
I'm not sure if that answers your question though. To "wait" for it you would need to put it in a async function with some setTimeouts.
I would like to know the difference between 2 implementations of callback functions.
This:
$("#button").on('click', function () {
//do something
});
Versus having the function already defined.
$("#button").on('click', btnFunction);
function btnFunction() {
//do something
}
Are there any implications with one compared to another? Performance-wise is one faster?
The first uses an anonymous function and the second does not. There's no difference in both.
See:
Why do you need to invoke an anonymous function on the same line?
Some folks prefer the second form because it gives a function name when using the debugger and tracing, but there are ways to get the same functionality in the first form.
If you are attaching and removing the event handler based on changing conditions, the second form is much easier to maintain, however.
There's no difference at all, and there's no performance issue with neither one of them. The only difference is that in one of them you're defining the callback function as an anonymous function, this way you cannot reuse it.
The other way, where you define it else where and named it and then pass it as a callback, you're defining a function that you can later reuse in another part of your code.
For example: if you want to do something when the document is ready, and then do se exact same thing when some one press a button you can use something like this:
function getData() {
//do something
}
$(function() {
// Call the function once the DOM is ready
getData();
});
// Call the same function when the button is clicked
$("#refresh_button").on('click', getData);
In most cases the first one will be used, called Anonymous Functions
The second one will be used when the function is not only used inlined here, but also needs to be reused somewhere else.
But anyway it could be a personal preference.
The only real difference you could see is that stack trace (if an exception is thrown for example) will be better, i.e. easier to debug, when using the second one.
Just reuse-ability.
In the second case, you could call btnFunction() somewhere else if need be.
Apologies - I have no idea to how to describe this. Example:
function OutputNumber(number) {
this.outputThisInstead = function (otherNumber) {
console.log(otherNumber);
}
console.log(number);
}
Desired usage:
new OutputNumber(1);
Console output: 1
new OutputNumber(1).outputThisInstead(2);
Console output: 2
Naturally, 1 will always be written to the console, irrespective of what else is called on the object.
I'm after this particular syntax, as well as the behaviour - attaching a function onto the initialisation. It feels impossible since the object must be constructed before any function is called on it, but is this achievable any other way?
It would be possible with a time delay (e.g., in a browser environment, setTimeout or similar) and a flag. Not desirable, but possible.
Without that, no, you can't base the action of the constructor on something that hasn't happened yet. You'd have to instead pass something into the constructor to let it know what was going on.
Browser example (again, I don't recommend this):
function OutputNumber(number) {
var handle = 0;
this.outputThisInstead = function (otherNumber) {
if (handle) {
clearTimeout(handle);
handle = 0;
}
console.log(otherNumber);
}
handle = setTimeout(function() {
console.log(number);
}, 0);
}
From your comment on the question:
This is the end of a sequence of chaining objects/functions, that I'm experimenting with. For example:
Assert.that(1).is.not(2).because('output this message if fails');
Here not(2) returns an object on which because can optionally be called. The behaviour of the object would depend on because being called.
Rather than have the behavior of an earlier function in the chain depend on a later function in the chain, I'd probably add an .end() at the end of something:
Assert.that(1).is.not(2).because('output this message if fails').end();
end would output whatever message/messages was/were stored by the previous functions. No need for black magic. Obviously this suffers from the fact that people could fail to put the .end() on, but you need some kind of trigger that it's okay to do the output, if you want the output to change based on an optional subsequent function call.
Not possible. By the time you do new OutputNumber(1) the function has already been called. A chained method will have no access to its preceding call.
It's possible to declare outputThisInstead as "static":
function OutputNumber(number) {
console.log(number);
}
OutputNumber.outputThisInstead = function (otherNumber) {
console.log(otherNumber);
}
new OutputNumber(1); //1
OutputNumber.outputThisInstead(2); //2
But if you want to create an object with the new operator the function will always log the number parameter.
You can also achieve similar behavior to the one you want with partial apply of the function (here). This is also called Currying or Schönfinkeling. The idea is that you can fill the function's parameters one after another and when the full set of parameters is available the function is being executed. You can see a currying example here.
Done a bit of Googling, but not quite finding what I want.
In an effort to reduce the amount of JAvascript lines my applications requires, I am trying to use as many reusable functions as possible.
The problem of course is trying to make these functions and flexible as possible.
So I have a form which is dynamically expanded by a cloning function using jQuery. In order for this to work, there are a few additional functions which need to run, for example, to correctly initialise datepickers, on the dynamically created elements.
These requirements are different depending upon which form is being cloned.
So, I call my cloning function as follows:
$('.button').click(function (){
var thisID = $(this).attr('id').replace('add', '');
cloneDetails(thisID, initialiseDates);
});
The cloneDetails function looks like this:
function cloneDetails(cur_num, callBackFunctions) {
/// do loads of stuff ///
callBackFunctions();
});
In this instance, the cloning function will run and do what it needs, then it'll run a second function call initialiseDates
What I want to be able to do though is specify several function names to run after the cloning function, with a call such as
cloneDetails(thisID, 'initialiseDates, doSomethingElse, doAnotherThing');
So, stating a list of functions to run after the cloneDetails function has been run.
I don't think I am setting up the callBack method correctly, and don't know how I should be.
Help much appreciated.
EDIT: I don't want to bundle all these functions into a single function, the whole point is these functions should all be useable independently and/or together.
I can think of several approaches:
You can split the string and use eval(name+'()') in each name in the list. Note that eval() can be evil, security wise.
Instead of a string, why not pass the functions as an array? Simply iterate over arguments[1..n] (arguments is an automatic variable which contains all function arguments).
Use an anonymous function:
cloneDetails(thisID, function(){
initialiseDates();
doSomethingElse();
doAnotherThing();
});
I like the last approach because:
It's the most flexible
Readable
Only adds a few bytes of code where you need them
Most performant
Another option (if you're using jQuery > 1.7): You can use the jQuery.Callbacks function.
cloneDetails(thisID, $.Callbacks()
.add(initialiseDates, doSomethingElse, doAnotherThing));
function cloneDetails(cur_num, callBackFunctions) {
/// do loads of stuff ///
callBackFunctions.fire();
});
Is it possible to edit a JavaScript function after the page has loaded?
I want to edit a function dynamically after Loading.
You can't edit a function, but you can replace it, e.g.:
var myFunc = function() { return "Hello World"; };
myFunc = function() { return "Goodbye"; };
javascript functions are objects, so can be replaced by setting a new value. Is this what you mean?
Unless you are trying to hack some code that doesn't belong to you, the better solution is to write a more flexible initial javascript function who's behavior can be adapted based on conditions (parameters passed, environment, other state, data fetched from other sources, etc...). Then, the one function you have can be written once initially to handle all your different circumstances.
You can even use design patterns such as passing in callback functions that can be used to adapt the behavior at runtime. If you desire many callbacks, you can pass in an object that has a number of different optional methods and call those during your function. In this way you can significantly alter the behavior of the main function without ever changing it's code by passing in different callback functions.
For example, let's assume we have a parsing function that takes some tagged data structure as input and returns an array of results. We want to be able to modify the behavior of this parsing function by passing in callbacks. So, we write the parsing function to take a callback object. That callback object contains one or more methods (all of which are optional) and a state variable that is passed to each callback. Anyone who has worked with ajax or any asynchronous networking in Javascript will recognize the callback object concept. Here's some pseudo code for such a process that shows how the callback object can be used. A real function would obviously be a lot more involved than this one, but it hopefully illustrates the concept:
function parseMyData(data, callbacks) {
var output = []; // output we accumulate
var currentTag;
callbacks = callbacks || {}; // make the callbacks object optional
// do any preprocessing that the caller specified
if (callbacks.preProcessData) {
data = callbacks.preProcessData(data, callbacks.state);
}
[[code to parse to the first tag in the data (after doing so currentTag contains the tag we just parsed)]]
// give our callback object the opportunity to do something to this tag or return null to skip it
if (callbacks.preProcessTag {
currentTag = callbacks.preprocessTag(currentTag, callbacks.state);
}
if (currentTag) {
[[code here for the default processing of the tag that will push results into the output array]]
}
return(output);
}
If you want to add an action to the existing function you can "hijack" it by putting it in a temporary variable and calling it within your overwritten function. E.g.
// The original function.
function sayName(name) {
alert(name);
}
// Temporary variable for original function.
var __sayHello = sayName;
// Overwrite the original function, adding extra actions.
sayName = function(name) {
alert('Hello');
// Call the original function using its temporary variable.
__sayHello(name);
}
// Call the overwritten function.
sayName('Bob');
How to edit a function - 101.
If we reconsider that editing a function at runtime is not absolutely changing the guts of the function, but rather changing what the function guts are digesting, then I would say functions are already built to do just that.
example 1 - The guts cannot be changed.
function one(){
return true;
}
// one() => true;
// Note: We could still change the output without changing the guts.
// !one() => false;
example 2 = The guts can be created to digest a dynamic call.
function one(payload){
return payload;
}
// one(true) => true;
// one(false) => false;
// one('whatever I want to feed the guts to digest');
// => 'whatever I want to feed the guts to digest';
These are quite simple examples, but since you did not provide any real examples as to what you are trying to do, we have to assume you are attempting normal patterns of programming.
Considering NORMAL patterns of programming, it wouldn't be the function itself that needs to change, rather how you are calling it.
example 3 - Give the choice of which function to the caller.
function firstChoice(payload){
return http.post(payload);
}
function secondChoice(choice){
return `Do something else with ${choice}`;
}
// And where you make the choice, perhaps after a click event handler...
function onClick(choice, payload){
choice ? firstChoice(payload) : secondChoice(choice);
}
Functions are supposed to be small bricks with which you build logic. Give them something small to do, then select between them based on your logic.
To answer your question, in my opinion, assuming normal programming needs...
"Is it possible to edit a JavaScript function after the page has loaded?" YES.
Use arguments in your function definition to add the dynamic ability to suit your needs.