How to create instance of javascript function without using arguments - javascript

I am using a JavaScript API of some software. In software there are Solutions and they contain Worklists.
For each Solution I want to list Worklists.
solutions = obtainSolutionsformAPI();
for (i=0,i<solutions.length,i++){
sol=solutions[i];
sol.obtainWorklists(callbackFunction);
}
the callbackFunction is called by software and the array of worklists is passed as the argument.
function callbackFunction(arrayOfWorkLists){
for (j=0,j<arrayOfWorkLists.length,j++){
wl=arrayOfWorkLists[j];
console.log(wl);
}
}
now in console.log I would like to also print out the i variable (the number of solution) together witch each worklist, but how do I get the i inside callback function, when I am not the one who is calling it? I suspect I need to modify the callback function each time before I pass it to sol.obtainWorklists(callbackFunction); What is the correct way of doing this?
Edit: I do not want the function defintions to be nested (one inside other), because I have multiple nestings like this one and it would be quite unreadable. So I do not want this:
sol.obtainWorklists(function callbackFunction(arrayOfWorkLists){...});

You can use a builder function:
sol.obtainWorklists(buildCallback(i, callbackFunction));
...where buildCallback looks like this:
function buildCallback(index, func) {
return function() {
var args = [index];
args.push.apply(args, arguments);
func.apply(this, args);
};
}
Complete example below
Then in your callback, expect the i value as the first argument with the arguments that obtainWorklists normally provides following it.
How that works:
When you call buildCallback, you pass in the i value and the callback you want called.
buildCallback returns a new function which has that information bound to it (it's a closure over the index and func arguments in the call to buildCallback).
When obtainWorklists calls the function buildCallback created, we create an array with the i value (index) followed by the arguments that were received from obtainWorklists.
Then we call the callback, using the same this value that the function was called with, and the args array; your callback will see the entries in args as discrete arguments.
If you don't care about the this value in your callback, you can use ES5's Function#bind instead:
sol.obtainWorklists(callbackFunction.bind(null, i));
That does much the same thing, but doesn't preserve the this used to call it.
Complete example for builder function (live copy):
// obtainWorklists stand-in
function obtainWorklists(func) {
setTimeout(function() {
// Just call it with a random number
func(rand(100, 200));
}, rand(100, 500));
}
// Code to get worklists
var i;
for (i = 0; i < 10; ++i) {
obtainWorklists(buildCallback(i, callback));
}
function buildCallback(index, func) {
return function() {
var args = [index];
args.push.apply(args, arguments);
func.apply(this, args);
};
}
// Your callback
function callback(index, value) {
display("Index is " + index + ", value is " + value);
}
// ========= Utility functions
function rand(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}
Complete example for Function#bind (live copy):
// obtainWorklists stand-in
function obtainWorklists(func) {
setTimeout(function() {
// Just call it with a random number
func(rand(100, 200));
}, rand(100, 500));
}
// Code to get worklists
var i;
for (i = 0; i < 10; ++i) {
obtainWorklists(callback.bind(null, i));
}
// Your callback
function callback(index, value) {
display("Index is " + index + ", value is " + value);
}
// ========= Utility functions
function rand(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function display(msg) {
var p = document.createElement('p');
p.innerHTML = String(msg);
document.body.appendChild(p);
}

You need to use a closure:
solutions = obtainSolutionsformAPI();
for (i=0,i<solutions.length,i++){
(function(innerI) {
sol=solutions[innerI];
sol.obtainWorklists(function(worklists) {
callbackFunction(worklists, innerI);
}
})(i);
}
function callbackFunction(arrayOfWorkLists, i){
console.log(i);
for (j=0,j<arrayOfWorkLists.length,j++){
wl=arrayOfWorkLists[j];
console.log(wl);
}
}

Related

What is the use of an argument list after a function body in javascript?

I came across this piece of code in using Dropzone JS and 3 arguments are repeated after the function body.
// Mock the file upload progress (only for the demo)
//
Dropzone.prototype.uploadFiles = function(files) {
var minSteps = 6;
var maxSteps = 60;
var timeBetweenSteps = 100;
var bytesPerStep = 100000;
var isUploadSuccess = Math.round(Math.random());
var self = this;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var totalSteps = Math.round(Math.min(maxSteps, Math.max(minSteps, file.size / bytesPerStep)));
for (var step = 0; step < totalSteps; step++) {
var duration = timeBetweenSteps * (step + 1);
setTimeout(function(file, totalSteps, step) {
return function() {
file.upload = {
progress: 100 * (step + 1) / totalSteps,
total: file.size,
bytesSent: (step + 1) * file.size / totalSteps
};
self.emit('uploadprogress', file, file.upload.progress, file.upload.bytesSent);
if (file.upload.progress == 100) {
if (isUploadSuccess) {
file.status = Dropzone.SUCCESS;
self.emit('success', file, 'success', null);
} else {
file.status = Dropzone.ERROR;
self.emit('error', file, 'Some upload error', null);
}
self.emit('complete', file);
self.processQueue();
}
};
}(file, totalSteps, step), duration);
}
}
};
At the end of the body, after the closing brace of the closure function(file, totalSteps, step) there is an additional list of arguments (file, totalSteps, step). I can't recall seeing this before so I'd like to know how they are used.
This is the simple explanation of your question:
What you have is a immediately-invoked function expression (IIFE). If you strip out all the arguments you're just left with this:
setTimeout(function () {
// some code
}(),5000);
It creates an anonymous (unnamed) function that accepts three parameters:
(function (file, totalSteps, step) {
// some code
}());
And you pass it three arguments:
(function (file, totalSteps, step) {
// some code
} (file, totalSteps, step));
The values passed will be set to the setTimeout function's argument
For example
setTimeout(function(a, b, c) {
console.log(a, b, c)
}(1, 2, 'hello'), 5000)
Its using closure: When you are writing reusable code or any library, and you don't want to mess up with outer or global variables, you pass those variables as arguments.
In this example, you are passing global variable as g and you are free to use global as variable name.
var global = "global";
(function (g){
var global = "local";
console.log("I am not overriden :" + g);
console.log("I am new incarnation :" + global);
})(global);
Let's say you have this function foo:
function foo(file, totalSteps, step) {
console.log(file, totalSteps, step);
};
And here's the function call:
foo(file, totalSteps, step);
But then, you decide to call it after 5000 milliseconds, use setTimeout:
setTimeout( foo(file, totalSteps, step) , 5000);
Now instead of defining the function foo outside, you decide to define it inside setTimeout by implementing anonymous-functions:
setTimeout(function(file, totalSteps, step) {
console.log(file, totalSteps, step);
}(file, totalSteps, step) , 5000);
The function you are passing to setTimeout is actually a function expression. Popularly known as IIFE (immediately invoked function expression).
setTimeout(fn expression, duration)
IIFE:
function name(){
}(param1, param2)
Now this function name has access to param1, param2 also this will be immediately invoked as soon as it gets created.
Basically the code is calling setTimeout's function to return another function which will called just after its definition with params file, totalSteps, step and returning. Maybe the coder have thought, that his way all params are on the same line for timer configuration: (file, totalSteps, step), duration).
Coders solution is quite complex and hard to follow. I think it would be better to avoid this kind of expression to improve readability. Also code doesn't have clearTimeout handling for setTimeout, so that code can cause problems in some environments. :)

When can a function be only called once?

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

Passing argument to function prototype

This piece of code was provided as a working solution to a previous question on flow control:
// an object to maintain a list of functions that can be called in sequence
// and to manage a completion count for each one
function Sequencer() {
this.list = [];
this.cnt = 0;
}
Sequencer.prototype.add = function(/* list of function references here */) {
this.list.push.apply(this.list, arguments);
}
Sequencer.prototype.next = function() {
var fn = this.list.shift();
if (fn) {
fn(this);
}
}
Sequencer.prototype.increment = function(n) {
n = n || 1;
this.cnt += n;
}
// when calling .decrement(), if the count gets to zero
// then the next function in the sequence will be called
Sequencer.prototype.decrement = function(n) {
n = n || 1;
this.cnt -= n;
if (this.cnt <= 0) {
this.cnt = 0;
this.next();
}
}
// your actual functions using the sequencer object
function test_1(seq, arg1, arg2, arg3) {
seq.increment();
// do something with arg1, arg2 and arg3
seq.decrement();
}
function test_2(seq) {
seq.increment();
// do something
seq.decrement();
}
function test_3(seq) {
seq.increment();
// do something
seq.decrement();
}
// code to run these in sequence
var s = new Sequencer();
// add all the functions to the sequencer
s.add(test_1, test_2, test_3);
// call the first one to initiate the process
s.next();
How can I pass arguments to test_1() when adding the function to s? For example (which obviously doesn't work):
s.add(test_1(10,'x',true), test_2);
Thanks
If the order was different, i.e. test_1(arg1, arg2, arg3, seq), you could use .bind:
s.add(test_1.bind(null, 10,'x',true) , test_2, test_3);
If you can't change the the order, pass an other function which in turn calls test_1:
s.add(function(seq) { test_1(seq, 10,'x',true); }, test_2, test_3);
You can access a function's given arguments with Function.arguments.

Calling function from array using setTimeout in Javascript

I am creating two function name call and call2 and putting this two functions in array x. In another function called timer i am trying to call this function every 2 seconds. But its giving error expected an assigment or function call instead show and expression.
And also i dont want this functions to run when i create an array.
this my code http://jsbin.com/IMiVadE/2/
function call(name)
{
console.log("Hello " + name);
}
function call2()
{
console.log("Hello world");
}
var x = [call("Nakib"), call2()];
var i = 0;
function timer(x, i)
{
x[i];
i++;
if(i<x.length)
window.setTimeout(timer(x, i), 2000);
}
timer(x, i);
You have some mistakes in your code:
call2() calls the function. Correct: call2 is the reference to the function.
x[i] accesses the value (the function reference). You need x[i]() to execute the function.
Here is your code working: http://jsbin.com/IMiVadE/6/edit
sayHello(name) is a function that generates new functions that output a specific "Hello ..." text.
function sayHello(name)
{
return function () {
console.log("Hello " + name);
}
}
function timer(x, i)
{
x[i]();
if (i < x.length - 1) {
setTimeout(function () {
timer(x, i + 1);
}, 2000);
}
}
var x = [sayHello("Nakib"), sayHello("world")];
setTimeout(function () {
timer(x, 0);
}, 2000);
setTimeout needs a function without parameter. So you can wrap the recursive call to timer in an anonymous function to fix that:
window.setTimeout(function(){timer(x, i)}, 2000);
Also, the first line in your timer function, consisting only of x[i];, is useless, although it probably isn't the cause of your problem.
The setTimeout function takes a function as a parameter, you are executing the timer function before it is passed and since timer doesn't return anything, undefined is being passed to the timeout;
window.setTimeout(timer(x, i), 2000); // is the same as...
window.setTimeout(undefined, 2000);
It should be;
window.setTimeout(function() { timer(x, i) }, 2000); // or...
window.setTimeout(timer.bind(this, x, i), 2000);
I'm not sure if this is intentional, but you are doing the same thing with your array;
var x = [call("Nakib"), call2()];
This will execute the functions and their results will be stored in the array. Is this what you want?

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