i was reading the source code for async.js library and with in that i found a function called 'only_once' and then i tried some examples to make it clear how it works, but i can't figure out what is wrong with my examples cause they simply do not behave the way they should. here is my code:
function only_once(fn) {
var called = false;
return function () {
if (called) throw new Error('Callback was already called.');
called = true;
fn.apply(this, arguments);
};
}
// my example code
var add = function (a, b) { return a + b; };
var add_once = only_once(add);
var a = add_once(1, 3);
console.log(a); // this prints undefined not 4, what the hell ?????
only_once() isn't currently doing anything with the result of fn -- add, in this case.
It should provide the return value from fn as its own:
return fn.apply(this, arguments);
// vs.
fn.apply(this, arguments);
In context:
// ...
return function () {
if (called) throw new Error('Callback was already called.');
called = true;
return fn.apply(this, arguments);
};
// ...
Within async.js, the return value isn't being used, so it doesn't have to be provided.
The asynchronous functions the library is designed for instead accept callback functions as arguments to be invoked later and passed the results.
only_once is just preventing these from being invoked multiple times:
function asyncFunc(callback) {
setTimeout(callback, 100); // callback invoked
setTimeout(callback, 200); // Uncaught Error: Callback was already called.
}
asyncFunc(only_once(function (value) {
console.log('callback invoked');
}));
Related
I came across David Walsh's once function here:
function once(fn, context) {
var result;
return function() {
if(fn) {
result = fn.apply(context || this, arguments);
fn = null;
}
return result;
};
}
// Usage
var canOnlyFireOnce = once(function() {
console.log('Fired!');
});
canOnlyFireOnce(); // "Fired!"
canOnlyFireOnce(); // nada
My question is what is the tracker variable here? Doesn't result become null each time canOnlyFireOnce() is fired? What is the purpose of setting the fn to null?
The fn is the tracker variable here. The fn variable holds the anonymous function. After calling the canOnlyFireOnce function once, the fn variable gets a new value "null". Now the fn(the anonymous function) is no longer available for execution, that's why you won't see the "Fired!" message again.
fn is the variable which tracks whether it's been called. You could write it this way with a more explicit extra variable:
function once(fn, context) {
var result;
var called = false;
return function() {
if (!called) {
result = fn.apply(context || this, arguments);
called = true;
}
return result;
};
}
I guess that setting fn = null can free some memory, so he wants to do that after the call too. So why not also use that to remember the call.
I want to attach a callback method to a javascript function in order to be called after its execution
Thanks in advance
When you can't avoid doing this (which is usually best), the usual way to do it is to wrap the function in another function:
function f(msg) {
console.log("The message is: " + msg);
}
f("Before being wrapped");
console.log("Now wrapping the function");
// Then wrapping it
var originalF = f;
f = function() {
var returnValue;
console.log("This is before the original function");
returnValue = originalF.apply(this, arguments);
console.log("This is after the original function");
return returnValue;
};
f("After being wrapped");
Note the use of Function#apply to ensure that we call the original function with the same this value we were called with and all of the original arguments. Then we keep the return value and return it when we're done.
Of course, if the original function started an asynchronous task, we've only wrapped the original function, not the code triggered when the task is complete.
You need to redefine the function. You can do this with an IIFE.
funcname = (function(oldfunc, callback) {
return function() {
var value = oldfunc.apply(this, arguments);
callback();
return value;
})(funcname, callback);
);
I'm testing my app, I have a dependency injection system that works like this (I paste here a very simplified version)
test.start = function(callback) {
// do stuff;
load(callback);
}
function load(callback) {
var args = ...// read callback's arguments
// args = ['moduleA', 'moduleB'];
var injectedArgs = args.map(function(a) {
return require('./lib/' + a);
});
// call function with deps injected
callback.apply(null, injectedArgs);
}
// test.js
test.start(function(moduleA, moduleB) {
moduleA.functionA();
moduleB.functionB();
});
What I need to do is an async job before calling the callback and start the test, in this particular case, that would be
test.start = function(callback) {
// do stuff;
load(function(moduleA, moduleB, moduleC) {
moduleC.resetData(function() {
return callback(moduleA, moduleB);
})
});
}
I need to make this dynamic, for all the test that have different modules as their callback arguments.
Requirements are
I don't want to change test definitions
I must call load only once per test
So basically I need to create the middle function that add moduleC argument, run the resetData function and call the original callback.
I can build the dynamically extended argument list using
var fn = new Function(newArgumentList, /*function body*/) but I can't attach the correct function body (using a string) because I lose the original callback context.
UPDATE
The DI library is modularity and it reads the callback definition to grab dependency. So basically, given the callback function(foo, bar) { ... } I need to create a new function(foo, bar, db) { db.resetData(function() { return callback(foo, bar) } )}
If load, itself, is something you can change (e.g., not part of the test definitions), you'll be glad to know this is quite easy to do: You use Function#apply:
function load(callback) {
var origCallback;
if (/*flag saying you need to do this*/) {
origCallback = callback;
callback = function() {
/*...do your injected work here...*/
return origCallback.apply(this, arguments); // `arguments` looks like pseudo-code, but it isn't; it's an identifier created in the function's scope (for non-arrow functions)
};
}
var args = ...// read callback's arguments
// args = ['moduleA', 'moduleB'];
var injectedArgs = args.map(function(a) {
return require('./lib/' + a);
});
// call function with deps injected
callback.apply(null, injectedArgs);
}
You can even do it if you can't change load, provided you can update the load symbol to make it point at a new function:
var oldload = load;
load = function() {
/*...do your reset work here...*/
return oldload.apply(this, arguments);
};
If you're curious, yes, you can update the symbols created by function declarations (and yes, that's per spec, not "tricky"). E.g., this is perfectly valid:
function foo() {
console.log("I'm foo");
}
var oldFoo = foo;
foo = function() {
console.log("I'm the new foo");
return oldFoo.apply(this, arguments);
};
foo(); // "I'm the new foo", then "I'm foo"
Example:
function foo() {
snippet.log("I'm foo");
}
var oldFoo = foo;
foo = function() {
snippet.log("I'm the new foo");
return oldFoo.apply(this, arguments);
};
foo(); // "I'm the new foo", then "I'm foo"
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
I found out the solution at the end.
Since modularity supports angular-like injection like this
['modules/a', 'modules/b', function(moduleA, moduleB) {
// use modules here
}]
I could write my custom callback like this
var customCallback = function() {
var args = Array.prototype.slice.call(arguments);
var injectedDBModule = args.pop();
injectedDBModule.resetData(function() {
// call the original callback function
return originalCallback.apply(null, args);
}
}
var loadCallback = originalInjectedModules; // ['moduleA', 'moduleB']
loadCallback.push('db');
loadCallback.push(customCallback);
modularity.load(loadCallback);
I have this spec from Jasmine.js which tests a once function. I'm not sure how to implement such a function though.
/* Functions that decorate other functions. These functions return a version of the function
with some changed behavior. */
// Given a function, return a new function will only run once, no matter how many times it's called
describe("once", function() {
it("should only increment num one time", function() {
var num = 0;
var increment = once(function() {
num++;
});
increment();
increment();
expect(num).toEqual(1);
});
});
I don't quite understand what should I do here. I know I should make a function once(myFunction) {} but other than that, I am stuck. I figure out this has something to do with closures, still can't my head around it.
If you prefer not to use UnderscoreJS, you can implement a simpler "once" function yourself like this:
var once = function (func) {
var result;
return function () {
if (func) {
result = func.apply(this, arguments);
func = null;
}
return result;
}
};
When you pass your function as the argument to this once function (as the parameter as 'func'), it returns a function that can only be called once.
It accomplishes this feat, in short, by creating a results variable and assigning that variable the results of calling your function with its supplied arguments--but only the first time it is run. Otherwise, when the function is invoked subsequent times, it will never enter your if statement (because the func variable was set to null in the first invocation) and the value referenced by the results variable (set during the first invocation and accessed via closure) will be returned.
Copied from the UnderscoreJS source:
_.once = function(func) {
var ran = false, memo;
return function() {
if (ran) return memo;
ran = true;
memo = func.apply(this, arguments);
func = null;
return memo;
};
};
http://underscorejs.org/docs/underscore.html
Very, very minimal
const once = fn => (...args) => {
if (!fn) return;
fn(...args);
fn = null;
};
(Old school version)
function once(fn) {
return function() {
if (!fn) return;
fn.apply(null, arguments);
fn = null;
}
}
var Obj = {
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be NOT called
}
return this;
},
_hasChainedFunc : function() {
// code which detects if there is a chained function???
}
}
Obj.func1().func2();
Is there a possible implementation for function _hasChainedFunc()? This function should return true on the first call (because func2() is called afterwards), false on the second call.
In a more advanced version, _hasChainedFunc() may also returned the function which is actually called afterwards.
Technically you can never know in advance whether there's another call chained after the current call -- this plainly doesn't make sense because it implies you're aware of some code that's gonna be called before it's called. You can't do this without a pre-compiler, which I guess is not what you're after.
Conversely, it is possible to check whether there's been a previous call chained before the current call. This just requires you to keep some state in the object regarding the previous calls, and update it whenever you call a new function on it. If you only use one chain of calls, you can do this by making func1 and func2 change some state on the this object before returning it.
If you want to call multiple chains on the same object, you face the problem of how to detect the end of a chain. For this you will need to make each chained function return a wrapper around the original this, which would store the state about the previous calls.
If you use the wrapper approach, obj.func1().func2() calls func1 on obj, but func2 is called on a wrapper returned from func1 and this wrapper could be aware of the previous func1 call. If you later call obj.func2().func1() then func2 is now called on obj whereas func1 is called on the wrapper which is aware of the previous func2 call, etc.
(NOTE: This answer was originally posted by Scholle as part of the question. I extracted it from the question into an actual answer, as it should have been in the first place. It is not my solution, so I have marked it as Community Wiki.)
Scholle ultimately created a library that does what he wanted.
It's available on GitHub, and some documentation is here.
In short: Take an arbitrary JavaScript function and "chainify" it:
var Model = function() {};
Model.prototype.func1 = function() {
console.log('func1 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func1 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func1 processing...');
this.c_next();
});
};
Model.prototype.func2 = function() {
console.log('func2 has ' + this.c_getPredecessors().length + ' preceding functions');
return this.c_delay().c_chain(function() {
console.log('func2 has ' + this.c_getSuccessors().length + ' succeeding functions');
console.log('func2 processing...');
this.c_next();
});
};
Chainify and instantiate it,
and call some functions:
chainify(Model);
var Obj = new Model();
Obj.func1().func2();
Console output:
func1 has 0 preceding functions
func2 has 1 preceding functions
func1 has 1 succeeding functions
func1 processing...
func2 has 0 succeeding functions
func2 processing...
Of course, this is a simple example. It just demonstrates that every
functions is now capable to access information about what happens
before and after the current function call.
No, it's not possible.
It's semantically identically to:
var tmp = Obj.func1();
tmp.func2();
When Obj.func1() is called, there's no way for it to know whether the subsequent result will be used to call func2.
The best you could achieve is for func2 to detect whether func1 was previously called, but for it to work the way you've described would require func1 to be capable of predicting the future.
What you can do is add a member property indicating if it's the first call made on the object or not:
var Obj = {
_first : true,
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be NOT called
}
return this;
},
_hasChainedFunc : function() {
var isFirst = this._first;
this._first = false;
return isFirst;
}
}
Obj.func1().func2();
However, this means you have to reset the state of the object before each call (by setting this._first back to true). You may want to rethink how you're going about this.
here's how i would do this:
var Obj = {
first:0, //<--- will store whether it's the first call
func1 : function() {
// some code
if (this._hasChainedFunc()) {
console.log("called1");
}
return this;
},
func2 : function() {
// some code
if (this._hasChainedFunc()) {
console.log("called2");
}
return this;
},
_hasChainedFunc : function() {
return (this.first++ > 0);
}
}
Obj.func1().func2();
and this seems to work:
called2
http://jsfiddle.net/2VThj/1/
Why would you want to do this?
That question aside, you could, rather than returning the actual object, make a clone of it, and add an attribute to tell you it is a returned version of the object. That is the only way I can think of. Sounds complex though, depending on how complex this object is.
Something like:
func1 : function() {
// some code
if (this._hasChainedFunc()) {
// block should be CALLED
}
return deepCloneWithFlag(this);
},
_hasChainedFunc : function() {
return this.flag;
}
Nope. this won't work. you could possibly tell that func1() had at some point been called on this object, but you cannot tell WHEN it was called, i.e. right before func2
for example this:
obj.func1();
obj.func2();
is equivalent to your example call. And there is no way func1 could know that func2 will be called in the future.
I solved a problem similar to this with chain functions (docs) This allows true function chaining with the ability to "look-ahead" to see what's coming in the chain.
What you could do is have two separate classes, one for the first element in the chain and one for the remaining elements. Then all you would have to do is change the first class to return an equivalent object from the second class instead of the current object.
var Class1 = function(state){
return {
func1 : function() {
// some code
// block should be CALLED
return Class2(state)
},
func2 : function() {
// some code
// block should be NOT called
return Class2(state)
}
};
}
var Class2 = function(state){
return {
func1 : function() {
// some code
return this;
},
func2 : function() {
// some code
return this;
}
};
}
Class1(initial_state).func1().func2();
Althought knowing that a function will be called after another function is impossible in Javascript, here is a solution to chainify an object :
(function(window, undefined)
{
var chainify = function(prop)
{
return new chainify.init(prop);
};
/**
*
* #param prop :
* Properties to apply to the object
* #returns {chainify.init}
*/
chainify.init = function(prop)
{
for ( var key in prop)
this[key] = prop[key];
};
chainify.init.prototype = {
_attributes : {},
_chain_in_progress : false,
_chain_level : 1,
_chain_function : '',
/**
* Returns the chained object
*
* #param name -
* name of the previous function
* #this chainify.init
* #returns {chainify.init}
*/
_chain : function(name)
{
var tmp = chainify(this);
tmp._chain_in_progress = true;
tmp._chain_function = name || '';
_chain_level++;
return tmp;
},
get : function(key)
{
return this._attributes[key];
},
set : function(key, value)
{
this._attributes[key] = value;
return this;
},
attr : function(prop)
{
for ( var key in prop)
this._attributes[key] = prop[key];
return this;
},
};
// Make global
window.chainify = chainify;
})(window);
var myObject = window.chainify({
// f1() function is using _chain()
f1 : function(s)
{
// Do something
this.set('s1', s);
if (this._chain_in_progress) alert('f1 after ' + this._chain_function);
// return the chain by calling this._chain()
return this._chain('f1');
},
// f2() function is using _chain()
f2 : function(s)
{
this.set('s2', s);
if (this._chain_in_progress) alert('f2 after ' + this._chain_function);
return this._chain('f1');
},
// that() function is not using _chain(), but we return this so the chaining
// is not broken
that : function(s)
{
// Do something
return this;
}
});
// Check if the f1 function is working
myObject.f1('a'); // Set s1 to "a"
alert(myObject.get('s1')); // should be "a"
// check if the f2 chaining is working
myObject.f1('b').f1('c'); // f1 after f1
alert(myObject.get('s1')); // should be "c" -> changed on last f1 function
// Check if the f2 function is working
myObject.f2('a');
alert(myObject.get('s2')); // should be "a"
// check if the f2 and f1 chaining is working
myObject.f2('b').f1('c').f1('d').f2('e'); // f1 after f2, f1 after f1 ...
alert(myObject.get('s1')); // should be "d" -> changed on last f1 function
alert(myObject.get('s2')); // should be "e" -> changed last f2 function
// check the chain with that() -
myObject.that('b').f1('a').f1('z'); // f1 chained after f1
alert(myObject.get('s1')); // should be "z" -> changed on last f1 function