When can a function be only called once? - javascript

This problem is giving me trouble:
Write a function, once, (see: http://underscorejs.org/#once) that
takes a function and returns a version of that function which can only
be called once. [Hint: you need a closure] You probably don't want to
be able to double charge someone's credit card. Here is an example of
how to use it:
var chargeCreditCard = function(num, price){
//charges credit card for a certain price
};
var processPaymentOnce = once(chargeCreditCard);
processPaymentOnce(123456789012, 200);
Here's how I tried to solve it:
var once = function(func) {
var invoked = 0;
return function() {
if (invoked === 0) {
invoked++;
return func();
}
};
};

The only problem I can see is you are not passing the arguments to the called function. You can use the arguments object and Function.apply() to do this.
var once = function (func) {
var invoked = 0;
return function () {
if (invoked === 0) {
invoked++;
return func.apply(this, arguments);
}
};
};
Demo: Fiddle

You are almost in the right path but you could also store the return value, pass the args and provide the this context:
function once(func) {
var val,
count = 2;
return function () {
if (--count > 0) {
val = func.apply(this, arguments);
} else {
//performance concern
func = null;
}
return val;
};
}
This is what I have borrowed from lodash to use in my codebase.
It is also worth noting that, passing the count variable as an argument would also let us to use it in a way that the func gets called less than count times

Related

is it possible to return a value from a function to a function parameter? js

so I noticed that when you use .addEventListener() you can get its event array like this:
document.getElementById("elementID").addEventListener("click",function(event) {
console.log(event);
});
Is there any way I can create my own function like this? I tried this before but I completely forgot how I managed to do it.
Right now I am trying
function foo(do) {
let a = 0;
setTimeout(do,1);
return a;
}
foo(function(a) {
console.log(a);
});
(results in undefined)
You can pass extra arguments to setTimeout, but note that updating the value in the function does not propagate it back (also note do is a reserved keyword so I changed the name to fn)
function foo(fn) {
let a = 0;
setTimeout(fn,1,a);
return a;
}
const result = foo(function(a) {
console.log(a);
a += 1
});
console.log(result);
So, you can pass an object in instead, and updating that will indeed propagate the change:
function foo(fn) {
let a = {value:0};
//setTimeout(fn,1,a);
fn(a)
return a.value;
}
const result = foo(function(a) {
console.log(a);
a.value += 1
});
console.log(result);
But note that I had to take out the setTimeout and replace it with a direct call to fn(a) - if I would have left that in, the function would have returned before the call to fn was made:
function foo(fn) {
let a = {value:0};
setTimeout(fn,1,a);
return a.value;
}
const result = foo(function(a) {
console.log(a);
a.value += 1
});
console.log(result);
Okay I found a solution:
function foo(event) {
setTimeout(function() {
var a = 0;
event(a);
},1);
}
foo(function(a) {
console.log(a);
});
logs 0
I forgot to mention that I am using code.org and coding with code.org is very very weird. It won't let you do a lot of really cool things like adding arguments for the setTimeout

function that makes another function only executable once in JS

I have been working to create a function that given another function will make that second function only callable once. not unlike the _.once() function.
the desired outcome is the following:
const oneTimeFunction = _.once(function(string) { string.split(''); })
oneTimeFunction('hello')
//returns: 'olleh', and if called again it would have no effect returning the same thing a the original call.
Currently this is what I have:
_.once = function (func) {
var called = 0;
let args = null;
if (arguments.length > 1) {
args = Array.prototype.slice.call(arguments,1);
}
return function () {
if (called === 0) {
console.log('being called');
called ++;
if (!args) {
console.log('without apply');
return func.call(arguments);
} else {
console.log('with apply');
return func.apply(this,args);
}
} else {
console.log('this has been called');
return null;
}
};
};
I am running into a wall as it is returning error type undefined even with everything I have tried. Any help, even to get to where it can call the function regardless of the one time only stipulation? Thanks!
create a variable that count how much this function is called
let count = 0;
function once(str) {
if(count < 1){
count++;
return str.split("").reverse().join("");
}
else return str;
}
console.log(once("hello")); // olleh
console.log(once("hello")); // hello
console.log(once("hello")); // hello
In reading your question, I'm seeing that you would like to always return the first value on subsequent calls:
"if called again it would have no effect returning the same thing a[s] the original call."
So I believe you want to do something like this:
function computeOnce(myFn) {
let origVal = undefined;
return function (...args) {
// if this is not set, this is the first call
if (!origVal) {
// execute the function and store it's return value
origVal = myFn(...args);
}
return origVal;
}
}

How would I detect and group recurring methods?

How would I go about detecting recurring methods and grouping them by an identifier when calls to the recurring methods are asynchronous?
To demonstrate the use of time taken before the callback is called, setTimeout is being used.
var counter = 0
var foo = (function () {
var context
return function foo (callback) {
if (!context) {
context = {id: ++counter}
}
setTimeout(function () {
callback.call(context)
context = null
}, 1)
}
}())
foo(function () {
console.log(1, this.id)
foo(function () {
console.log(2, this.id)
})
})
foo(function () {
console.log(3, this.id)
})
The above code produces:
1 1
3 undefined
2 undefined
The desired result is:
1 1
3 2
2 1
Ideally, this would be achieved without having to use anything like .bind on the foo calls.
I've experimented briefly with arguments (more specifically arguments.callee) and am aware I most likely need some way of duplicating foo with different ids, though I couldn't get any results which persisted the id on the arguments.callee function returned.
EDIT: Thanks for the answers so far! These are perfect answers for the question, but my use case does take a step further.
In the current scenario, the callback may be called asynchronously at an indeterminate time, meaning context slides back to null before I need it to.
I've edited the above code and explanation to reflect that new issue.
You could create a variable in a closure around the foo method that stores if the method is currently "being called". It's hard to explain, but this is what it would look like:
var foo = (function() {
var locked;
return function (callback) {
if (!locked) {
counter += 1;
this.id = counter;
}
locked = true;
// Any time this callback calls 'foo',
// it will see it is locked and not increase the id
callback();
locked = false;
}
}());
var counter = 0;
var foo = (function() {
var locked;
return function(callback) {
if (!locked) {
counter += 1;
this.id = counter;
}
locked = true;
// Any time this callback calls 'foo',
// it will see it is locked and not increase the id
callback();
locked = false;
}
}());
foo(function() {
log([1, this.id])
foo(function() {
log([2, this.id])
})
})
foo(function() {
log([3, this.id])
})
function log(msgs) {
document.body.insertAdjacentHTML("beforeend", "<code>" + msgs.join(" ") + "</code><br />"); }

A function that takes a callback and creates a new version of the callback that can only be called once. Javascript

I trying to create a function, that takes another function as the argument, and creates a new version of the callback function that can only be called once. Subsequent calls will return the output if the initial call.
This is along the lines of recreating the Underscore .once method.
Here is what I have thus far. I have created a chargeCreditCard function. I want to create a new version of this function that can only be called once (chargeOnce). Explanation is appreciated. Thanks.
Edit. I want the once function to not rely on any code outside of the function to work (ie. an external counter variable).
var chargeCreditCard = function(num, price){
return num*price;
};
function once (func) {
var hasActionBeenCalled = false;
var call = function () {
if(!hasActionBeenCalled) {
hasActionBeenCalled = true;
func;
}
}
}
var chargeOnce = once(chargeCreditCard);
console.log(chargeOnce(2,3));
console.log(chargeOnce(4,5));
Your function once does not return anything, and your function call does not call anything. Make it
function once(func) {
var hasActionBeenCalled = false;
return function() {
if (!hasActionBeenCalled) {
hasActionBeenCalled = true;
return func.apply(this, arguments);
}
}
}
For garbage collection, I'd recommend to do
function once(func) {
var res;
return function() {
if (typeof func == "function") {
res = func.apply(this, arguments);
func = null; // unset func
}
return res;
}
}

Updating an array inside a call back

I have the following two functions:
var abc;
function updateNum() {
abc=0;
g.dbm.transaction("leagues").objectStore("leagues").openCursor(null, "prev").onsuccess = function (event) {
var teams, i;
team.filter({
attrs: ["tid", "abbrev", "region", "name", "cid"],
seasonAttrs: ["winp", "playoffRoundsWon"],
season: g.season
}, function (teams) {
// Sort teams by playoffs and winp, for first round
teams.sort(function (a, b) {
if (a.playoffRoundsWon < b.playoffRoundsWon) {
return -1;
}
if (a.playoffRoundsWon > b.playoffRoundsWon) {
return 1;
}
return a.winp - b.winp;
});
abc+=1;
});
};
}
function getNum() {
return abc;
}
What I am trying to do is update the variable abc inside the callback function and then return it. I do this by first calling the updateNum() function in another file. Then I assign a variable to the value of getNum()
Here is how a sample code would look like:
myFile.updateNum();
var number = myFile.getNum();
I am currently unable to return the updated value of num. number keeps returning 0 (the default value) instead of the newly updated value (which is 1).
How can I get it to show an updated value? Please let me know if I need to add any more information.
Well, if updateNum is async, it would have to take a callback as argument so that you can be notified when the number was updated.
E.g.
var num = 0;
function updateNumAsync(callback) {
setTimeout(function () {
num = 1;
callback && callback(num); //call the callback if provided
}, 500);
}
updateNumAsync(function (num) {
console.log(num); //updated num
});
Here is a general pattern for using an asynchronous function with a callback to pass the asynchronous results around. What is team.filter? You will need to design your code such that the asynchronous portion calls a callback() function that was passed to the enclosing function.
If filtering gives you problems you may want to look at https://github.com/caolan/async#filterarr-iterator-callback
(function main(){
getNum(function(err, abc){
console.log('thx for playing '+abc)
});
})();
function getNum(anotherCallback) {
// Whatever code relies on the result of an asynchronous function must be
// placed inside the callback function
countTeams(function(abc){
console.log('countTeams completed, abc='+abc);
var err = null;
anotherCallback(err, abc);
});
};
function countTeams(callback){
var abc=0;
g.dbm.transaction("leagues").objectStore("leagues").openCursor(null, "prev").onsuccess = function (event) {
var teams, i;
// I don't know what this filter function does, I am assuming it's synchronous
team.filter({
attrs: ["tid", "abbrev", "region", "name", "cid"],
seasonAttrs: ["winp", "playoffRoundsWon"],
season: g.season
}, function (teams) {
// Sort teams by playoffs and winp, for first round
teams.sort(function (a, b) {
if (a.playoffRoundsWon < b.playoffRoundsWon) {
return -1;
}
if (a.playoffRoundsWon > b.playoffRoundsWon) {
return 1;
}
return a.winp - b.winp;
});
abc+=1;
});
return callback(abc); // 0 or n depending on what team.filter does
};
};

Categories

Resources