I've found myself using this pattern recently to do initialization that should only ever run once:
function myInit() {
// do some initialization routines
myInit = function () {return;};
}
This way if I had two different methods which required something myInit did, it would ensure that it would only run once. See:
function optionA() { myInit(); doA(); }
function optionB() { myInit(); doB(); }
In the back of my head I feel like I'm missing something and I shouldn't be doing this. Is there any reasons why I shouldn't write code like this?
Is there any reasons why I shouldn't write code like this?
One reason is that the function will only work as you intend in the scope it was defined in. E.g. if you pass the function somewhere else, it won't be affected by your modifications and in the worst case would create an implicit global variable. E.g.
function myInit() {
// do some initialization routines
myInit = function () {return;};
}
function foo(init) {
init();
init();
}
foo(myInit);
The better approach is to encapsulate the whole logic:
var myInit = (function() {
var initialized = false;
return function() {
if (initialized) return;
initialized = true;
// do some initialization routines
};
}());
Now, no matter how, where and when you call myInit, it will do the initialization step only once.
May be you can do something like,
var myInitCalled = false; // Global declaration of flag variable
function myInit() {
// do some initialization routines
myInitCalled = true; // Setting the global variable as true if the method is executed
myInit = function () {return;};
}
Then in your methods, you can probably use:
function optionA()
{
if(!myInitCalled ) // Checking if first time this is called.
{myInit();}
doA();
}
function optionB()
{
if(!myInitCalled )
{myInit();}
doB();
}
This will ensure that myInit is called only once!!
Related
I have an interceptor function that I can put some javascript code into (for swagger ui). I only have one place to put in javascript code and it will be re-run frequently. I need to add a mutating observer in there, and have it only be setup one time.
There are many examples of how to do a Javascript method that will allow your code to be only run once. Here is a popular example: Function in JavaScript that can be called only once
But it assumes that you have somewhere to put the function where it will only be called once.
Is there a way to have my javascript code (not necessarily a function) only be run once? (So I don't end up setting up a bunch of mutating observers?)
To illustrate with an example, here is the code from the question I linked to, but copied in twice to show that it would be run many times:
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
console.log("hello");
}
};
})();
something(); // "do something" happens
something(); // nothing happens
var something = (function() {
var executed = false;
return function() {
if (!executed) {
executed = true;
console.log("hello");
}
};
})();
something(); // "do something" happens
something(); // nothing happens
The output of this code is:
hello
hello
Because the function is initalized twice, the call to console.log happens twice.
I need some way to have my code only happen once, with only one place to declare it.
What about storing executed value in document ... it is created once at execution ... it could be stored in window (i.e. global var) but I think it is better to be kept in document. I'm not familiar with swagger but docuument should always exits I think. Storing it in localStorage will keep it longer than you may need and you should reset it on each run.
var something = (function() {
return function() {
if (!document.executed) {
document.executed = true;
console.log("hello");
}
};
})();
something(); // "do something" happens
something(); // nothing happens
var something = (function() {
return function() {
if (!document.executed) {
document.executed = true;
console.log("hello");
}
};
})();
something(); // "do something" happens
something(); // nothing happens
For browser environment, you can use localStorage to set the value.
// make sure to initialize with true in your main entry point, otherwise the function would not run for the first time
localStorage.setItem('run-function', true);
const run = localStorage.getItem('run-function');
function test() {
if (run) {
console.log('ran function');
// ran once, set the value to false to prevent dupes
localStorage.setItem('run-function', false);
} else {
console.log('function already called once, exiting');
}
}
test();
if you're on node.js environment you can make use of the global variable
// make sure to initialize with true in your main entry point, otherwise the function would not run for the first time
global.run_function = true;
function test() {
if (global.run_function) {
console.log('ran function');
global.run_function = false;
} else {
console.log('function already called once, exiting');
}
}
test();
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
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
);
};
I'm trying to figure out how I can reset a timer created inside of an immediately invoking function from within the setTimeout closure. Here is my function:
var triggerHeightRecalc = function() {
setTimeout(function() {
if(imagesLoaded()) {
adjustHeight();
} else {
triggerHeightRecalc();
}
}, 100);
}();
In the event that imagesLoaded() returns false, I receive the following error from attempting to call triggerHeightRecalc():
Uncaught TypeError: undefined is not a function
So I'm not sure if the issue is the function is not in the scope, or maybe it just cannot call itself? I've tried passing triggerHeightRecalc as a parameter in the setTimeout closure, but that doesn't seem to work either.
I've also tried this after reading this SO question:
var triggerHeightRecalc = function() {
var that = this;
var callback = function() {
if(imagesLoaded()) {
adjustHeight();
} else {
that.triggerHeightRecalc();
}
};
timeDelay = window.setTimeout(callback, 100);
}();
What am I doing wrong here, or is there a better way? Is this something that should be a setInterval() instead and I clear the interval when images are loaded?
Side Note: I'm calculating the height of a div inside a jQuery plugin, but I need to wait until the images are loaded in order to get the correct height (not sure if that is relevant).
Since you are invoking the function right from the declaration triggerHeightRecalc is getting set to the return of that function call, which is undefined since you in fact do not return anything.
You can do two things
1. Declare then invoke
var triggerHeightRecalc = function() {
setTimeout(function() {
if(imagesLoaded()) {
adjustHeight();
} else {
triggerHeightRecalc();
}
}, 100);
};
triggerHeightRecalc();
2. Wrap the declaration in () and invoke
var triggerHeightRecalc;
(triggerHeightRecalc = function() {
setTimeout(function() {
if(imagesLoaded()) {
adjustHeight();
} else {
triggerHeightRecalc();
}
}, 100);
})();
The second one will create a global variable unless you do the var triggerHeightRecalc; before hand.
Already answered, but I'll put this in.
First of all, if you just want to wait until all images have loaded you can use:
https://github.com/desandro/imagesloaded and then run the above code.
If that's not what you want, and you you just want a function that your setTimeout can run, then you can remove the () at the end of the function.
Here is what's happening in your current code
Your function is missing the opening bracket or similar character !+( (function.
Also your IIFE has no return keyword, and will return undefined to triggerHeightCalc.
If you do want an IIFE then you can either have a private version that is only callable within itself.
(function myModule(){
myModule(); //calls itself
})();
Or a public version that can be called both inside and outside.
var myModule = (function(){
return function myMod(){
myMod();
}
})();
myModule();
Patrick Evans has the right reasons, but there is a neater way to solve it :)
(function triggerHeightRecalc() {
setTimeout(function() {
if(imagesLoaded()) {
adjustHeight();
} else {
triggerHeightRecalc();
}
}, 100);
})();
Here you are give an internal name to the (still) anonymous function. The name is only visible from within the function itself, its not visible in the global scope. Its called a Named function expression.
I am looking for a good technique to get away from what I am tempted to do: to set a global variable.
The first time someone runs a function by clicking a button it triggers an initial function to turn a few things into draggables. Later, if they click the button a second time I want to determine if the init function has been initialized, and if so to not call it again. I could easily do this by setting a global variable from the init function and then checking that variable from the click function, but I'm wondering how to do this without setting a global variable. I would really like an example of a way to do this.
You could add a property to the function:
function init() {
init.called = true;
}
init();
if(init.called) {
//stuff
}
While #Levi's answer ought to work just fine, I would like to present another option. You would over write the init function to do nothing once it has been called.
var init = function () {
// do the initializing
init = function() {
return false;
}
};
The function when called the first time will do the init. It will then immediately overwrite itself to return false the next time its called. The second time the function is called, the function body will only contain return false.
For more reading: http://www.ericfeminella.com/blog/2011/11/19/function-overwriting-in-javascript/
Why don't you just check to see if your draggables have a class of draggable on them?
if ($('.mydiv').is('.draggable')) {
//do something
}
Function.prototype.fired = false;
function myfunc() {
myfunc.fired = true;
// your stuff
};
console.log(myfunc.fired) // false
myfunc();
console.log(myfunc.fired) // true
What you could do is unhook the init function from the prototype.
var Obj = function () {
this.init = function () {
document.write("init called<br/>");
this.init = null;
}
}
var o = new Obj();
if (o.init) document.write("exists!<br/>");
o.init();
if (o.init) document.write("exists!<br/>");
o.init();
The first if will be true and print exists! but since the function removes itself, the second if will fail. In my example, I call the second init unconditionally just to show that nothing will happen, but of course you could call it only if it exists:
if (o.init) o.init();
http://jsfiddle.net/coreyog/Wd3Q2/
The correct approach is to use the Javascript Proxy APIs to trap the function calls using apply handler.
const initFun = (args) => {
console.log('args', args);
}
const init = new Proxy(initFun, {
apply(target, thisArg, args){
target.calls = target.calls ? target.calls + 1 : 1;
return target.apply(thisArg, args);
}
});
init('hi');
console.log(init.calls); // 1
init('hello');
console.log(init.calls); // 2