I would like to bind or chain a callback/function to another function.
There is a similar question (without any valid answer): jQuery , bind a callback to any function
But, for me I would not want to be limited only to jQuery realm.
I looking for a broader answer, I wonder if it's possible with vanilla javascript or library other than jQuery.
Example:
// a function which I don't want or I can't edit it's body
var thirdPartyObject = {
doSomething: function(args) {
// a lot of code
console.log('doing stuff: ' + args);
}
};
// my function
var easyCallback = function() {
// processing stuff
console.log('doing more stuff');
}
// the bind
magicLibrary.bind(thirdPartyObject.doSomething, easyCallback);
// run
thirdPartyObject.doSomething(1);
thirdPartyObject.doSomething(10);
When I run this "code", the following output represents the behaviour I'm looking for:
doing stuff: 1
doing more stuff
doing stuff: 10
doing more stuff
Is it possible?
EDIT: the bind is a conceptual term, maybe you think this like a chain, trigger or even another term.
But the import is the second function or callback which in my example is easyCallback() must be somehow connected to the first one doSomething().
And every time the doSomething() is called or executed I want the easyCallback() to be executed as well after the first is finished.
But the without "wrapping" them around or without rewriting the first one.
You would have to wrap the doSomething function inside yet another function, like so:
// a function which I don't want or I can't edit it's body
var thirdPartyObject = {
doSomething: function(args) {
// a lot of code
console.log('doing stuff: ' + args);
}
};
// my function
var easyCallback = function() {
// processing stuff
console.log('doing more stuff');
}
// the bind
// magicLibrary.bind(thirdPartyObject.doSomething, easyCallback);
const doSomething = thirdPartyObject.doSomething.bind(thirdPartyObject);
thirdPartyObject.doSomething = function(args) {
doSomething(args);
easyCallback(args);
};
// run
thirdPartyObject.doSomething(1);
thirdPartyObject.doSomething(10);
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 javascript below, which appends a DIV on page load and hides it after 3 sec.
var testObj = {
initialize: function() {
var that = this;
$(window).on("load", function() {
(function ($) { //Append Div
$('body').append("<div>TEST</div>");
})(jQuery);
that.hideAppendedDiv();
});
},
hideAppendedDiv: function() { //Hide appended Div after 3s
setTimeout(function(){
$("div").hide();
}, 3000);
}
};
//call Initialize method
testObj.initialize();
How to write Jasmine test cases for the methods in the code.
I'm guessing that you don't really want to test a Javascript function such as $(window).on('load')... , but that your own function hideAppendedDiv() get's called from $(window).on('load'). Furthermore, you want to make sure that the function hideAppendedDiv() works as well.
IMO, you need two expects.
Somewhere in your setup beforeEach function:
beforeEach(function () {
spyOn(testObj , 'hideAppendedDiv').and.callThrough();
});
Expectations
it('expects hideAppendedDiv() to have been called', function () {
// make the call to the initialize function
testObj.initialize ();
// Check internal function
expect(testObj.hideAppendedDiv).toHaveBeenCalled();
});
it('expects hideAppendedDiv() to hide div', function () {
// make the call to the hideAppendedDiv function
testObj.hideAppendedDiv();
// Check behavior
expect(... check the div ...)
});
Edit
Just to be clear, Jasmine executes all the expects in order. Now, if you have two functions fn_1(), and fn_2() and you want to test that they were called in order you can setup yet another spi function that returns a specific value, or a sequential and incremental set of values every time it is called.
beforeEach(function () {
spyOn(testObj , 'fn_1').and.returnValues(1, 2, 3);
spyOn(testObj , 'fn_2').and.returnValues(4, 5, 6);
});
The first time fn_1 is called it will return 1, respectively fn_2 will return 4.
That is just one of the ways, but you have to get creative when testing.
Now if you want to test that a function was called after x amount of time here is a post that already explains it.
You don't need to test the window load event, if you move the append code out of the anonymous function call and pass it into the event handler instead you can test the functionality in exactly the same way you would anything else and your code will be better structured.
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!!
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