function updateServerList() {
var i;
for (i=0; i < servers.length; i++) {
var server = servers[i];
var ip = server['serverIp']
var html = constructServer(i);
var divId = '#server' + ip.replace(new RegExp("\\.", "mg"), "-");
var visible = $(divId).find(".server_body").is(":visible");
var div = $(divId);
div.html(html);
// Set div class.
var prevState = div.attr('class').substring(7)
if (prevState != server['state']) {
if (server['state'] == 'ok') {
console.debug(server);
div.slideUp('fast', function(server) {
$(this).removeClass();
$(this).addClass('server_ok');
var id = ipToId[server['serverIp']];
console.debug(id);
if (id == 0) {
adjacentIp = servers[1]['serverIp'];
adjacentDivId = '#server' + adjacentIp.replace(new RegExp('\\.', 'g'), '-');
$(adjacentDivId).before(this);
}
}).delay(1000);
div.slideDown();
}
}
}
console.debug shows server as being defined, but inside the anonymous function, server is not defined. What am I going wrong?
because server is an argument to the function, its masking the value of the server at the higher level. You need to either pass server to the function, or remove the function argument. I would do the latter, as slideUp doesn't give you a way to pass arguments. You could do it but its needlessly complicated; it would look something like the following
div.slideUp('fast', (function(server) {
return function(){
// your stuff here, server is now 'closed in', i.e. in a closure
}
})(server)); // <-- this server is the current value in the loop
what you are doing here is invoking a new function right away, passing in the argument server, and returning a new function that receives that value.
var server = servers[i];
var prevState = div.attr('class').substring(7);
if (prevState != server['state']) {
if (server['state'] == 'ok') {
console.debug(server);
div.slideUp('fast', function() {
...
var id = ipToId[server['serverIp']];
}
}
Inside your anonymous function, "server" is still within the function scope. No need to pass it in as an argument.
The Quick Fix
// ...
div.slideUp('fast', function() { // `server` argument removed
// ...
});
The Explanation
There is no need to pass server to the function. The anonymous function "closes" over the server variable.
This is merely a function declaration:
function (server) {...}
You aren't passing anything to the function yet, as it isn't being invoked yet! The (server) bit
in a function declaration simply lets you name the arguments to your function. Only when you invoke the function can you pass arguments:
var name = "Jill";
var showName = function (name) {
alert(name);
};
showName("Jack"); // alert box shows "Jack"
showName(); // alert box shows "undefined"
So, when you declare that the name of the first argument to your anonymous function is server, there is a name conflict which prevents the original from being accessible; the server in your anonymous function is whatever slideUp passes as the first argument, which, according to the documentation, is nothing, so server is now undefined.
If this is confusing (and I suspect it is), I would suggest reading about javascript closures. Here's a good place to get started.
Fun fact: you can actually access arguments, in order, without having any explicit names, by using Javascript's built in arguments array object inside a function:
var sum = function () {
var i, total = 0;
for(i = 0; i < arguments.length; ++i) {
total = total + arguments[i];
}
return total ;
};
alert(sum(1,2,3)); // Displays "6"
alert(sum(1,2,3,4)); // Displays "10"
alert(sum(1,0,2,3)); // Displays "6"
alert(sum()); // Displays "0"
Related
How can I pass a parameter to a function within CasperJS's evaluate?
//Should be logged in at this point
casper.then(function() {
var counter = 0;
var cap = 500;
this.evaluate(function(counter) {
var children = $('.companies-using-service').children();
while (counter < children.length) {
child = children[counter];
console.log($(child).find('a').attr('data-hint'));
counter++;
}
}, counter);
});
};
var scrapeClients = function(counter) {
var children = $('.companies-using-service').children();
while (counter < children.length) {
child = children[counter];
console.log($(child).find('a').attr('data-hint'));
counter++;
}
}
Above, I am able to pass parameters in using an unamed function. However, I wish to pass in the function scrapeClients to the evaluate function. In that case, I tried the following this.evaluate(scrapeClients(counter), counter). However, this does not work and the error says that it could not find $ variable.
Functions are first-class citizen in JavaScript. You can treat them in the same way as variables. You can pass them around. This means that you don't want
this.evaluate(scrapeClients(counter), counter)
but rather
this.evaluate(scrapeClients, counter)
In the first case, you're actually calling the function directly. Since the function uses some page properties that are only available inside of casper.evaluate, this will throw an error and stop the script.
How does one pass a variable to one function and then return a value to another function?
I have simple example here where I am trying to work it out. The first function accepts the argument 'currpage', returns it to the second function correctly as a number, but then when I call the second function, console.log shows NaN. Not sure what I might be doing wrong.
$(document).ready(function () {
var currpage = 1;
$('p').click(function () {
var xyz = passfrom(currpage); //pass var to function and return value
console.log(xyz); //returns correct value
var abc = passto();
console.log(abc); //NaN
})
})
function passfrom(currpage) {
var newpage = parseInt(currpage) * 1000;
return newpage;
}
function passto() {
var newcurr = passfrom();
var newcurr = newcurr * 1000;
console.log(typeof (newcurr)); //number
return newcurr;
}
How does one pass a variable to one function and then return a value to another function?
A variable is just a little container where you store a value. When you pass a variable to a function, you really pass the value of that variable to it. So in your case:
passfrom(curpage);
and
passfrom(1);
are the same.
Within a function, variable names are used to access these values. These names are totally independent of whatever name was attached to the value outside the function (if it even had a name). They are more like aliases. To distinguish them from variables, we call them parameters. So this one:
function passfrom(currpage) {
var newpage = parseInt(currpage)*1000;
return newpage;
}
and this one:
function passfrom(myownname) {
var newpage = parseInt(myownname)*1000;
return newpage;
}
are exactly the same. And if we were to write out what actually happens, we'd get this:
// var xyz = passfrom(currpage);
var xyz = value-of(passfrom(value-of(currpage))
So all you have to do to pass a value to some function, is to make sure that it has such a parameter name available by which it can use that value:
function passto(myalias) {
console.log(myalias);
}
passto(xyz); // writes 1000 to the console.
The above is the actual answer to your question.
To make things a little bit more complicated, there are two more things to take into account:
Scope. The parameter names only work within your function. If they are the same as some variable name outside the function, that outside variable is hidden by the parameter. So:
var currpage = 1;
function plusOne(currpage) { curpage += 1; }
plusOne(currpage);
console.log(currpage); // 1, as the variable currpage was hidden
function plusTwo(othername) ( currpage += 2; }
plusTwo(currpage);
console.log(currpage); // 3, as currpage was not hidden
This all works for strings, integers, and other simple types. When you're dealing with more complex types, the parameter name isn't an alias for the value passed to the function, but for the location of the original value. So in that case, whatever you do with the parameter within the function will automatically happen to the variable outside the function:
var arr = [ 0, 1 ];
function plusOne(somearr) { somearr[0] += 1; }
plusOne(arr);
console.log(arr[0]); // 1, as somearr references arr directly
This is called "pass-by-value" and "pass-by-reference."
You're dealing with two different currpage variables, causing one to be undefined when you try to perform arithmetic on it, resulting in a NaN result. See my inline code comments below for further explanation:
$(document).ready(function() {
var currpage=1; // Local to this function, because of the var keyword.
...
})
}) // I'm assuming this extra closing brace and paren is a typo.
// Otherwise, your code example has a syntax error or is incomplete.
function passfrom(currpage) {
// currpage is the name of the parameter to passfrom.
// It happens to have the same name as a local var in
// the document.ready callback above, but the two are
// not the same.
var newpage = parseInt(currpage)*1000;
return newpage;
}
function passto() {
// passfrom is called with an implicit 'undefined' argument.
// Thus, undefined will be used in the arithmetic ops and produce NaN.
var newcurr = passfrom();
// Don't need the var keyword below.
var newcurr = newcurr * 1000;
console.log(typeof(newcurr)); //number
return newcurr;
}
You need to make the same currpage variable accessible from both passfrom and passto by putting it in a higher/more global scope or move those functions into the same scope that the original currpage is in. Something like this:
var currpage;
$(document).ready(function () {
$('p').click(function () {
var xyz = passfrom(1); //pass var to function and return value
console.log(xyz); //returns correct value
var abc = passto();
console.log(abc); //NaN
})
})
// Rename the param so there isn't a naming conflict.
function passfrom(currpageParam) {
// If the param is a number, reset the global var.
if (typeof currpageParam == 'number') { currpage = currpageArg; }
var newpage = parseInt(currpage) * 1000;
return newpage;
}
function passto() {
var newcurr = passfrom();
newcurr = newcurr * 1000;
console.log(typeof (newcurr)); //number
return newcurr;
}
Be careful though. You'll probably want to take steps to protect your currpage var from outside modification. Also, I suspect that there's a better way to do what you're trying to do, but it isn't clear exactly what that is, so I can't suggest anything.
var sc = new stuCore();
function stuCore() {
this.readyPages = [];
this.once = true;
var self = this;
// gets called asynchronously
this.doPrepPage = function (page){
if(self.once == true){
// still gets executed every time, assignment fails
self.once = false;
doSomeStuffOnce();
}
};
this.addReadyPage = function (pageid) {
console.log("readypage called");
this.readyPages.push(pageid);
if (!$.inArray(pageid, self.readyPages) != -1) {
this.doPrepPage(pageid);
}
};
}
why does this assignment fail? I thought I knew the basics of js, but I'm stumped by this. And furthermore what would be a possible solution? call a constructor first and set the variable there?
EDIT:
gets called like this in some other script:
sc.addReadyPage(self.id);
The jQuery.inArray function will return the index in the containing array for the given value. Your script pushes pageid into this.readyPages before checking whether it exists in self.readyPages. this.readyPages and self.readyPages are the same array reference, so the result will always be zero or greater, so the condition that calls doPrepPage will never run.
You could try switching their order around:
this.addReadyPage = function (pageid) {
console.log("readypage called");
if ($.inArray(pageid, self.readyPages) != -1) {
this.readyPages.push(pageid);
this.doPrepPage(pageid);
}
};
(edit: Removed the additional !, thanks #chumkiu)
If I understand correctly you're calling this.doPrepPage as <insert variable name here>.doPrepPage?
If this is the case then your var self passes through to the anonymous function and is stored there, so everytime you call this.doPrepPage it takes the local variable of self.
Try setting self to a global variable, this way it will permanently modify self so each time this.doPrepPage is called it uses the updated variable.
I have a for loop in a search function, with a function that does a callback inside the loop, and I want to execute a BUILD() function after the loop, and after all the callbacks are completed. I am not sure how to do that, because the loop finishes before all the callbacks are done. The callbacks are API requests to get me data, and I want to BUILD() with that data.
I read up on deferred, so I tried to put the for loop inside a function to the deferred, and then calling BUILD() on '.then( ... )'. But that doesn't seem to work - I think I am understanding it wrong.
HELP?!
Note, this is using the Google Maps Places API (search and getDetails).
var types = {
'gym' : 'fitness, gym',
'grocery_or_supermarket': ''
}
function search() {
for (var key in types) {
var request = { ... };
service.search(request, searchCallback);
}
// PROBLEM AREA
BUILD();
}
function searchCallback(results, status) {
for (var i = 0; i < results.length; i++) {
var request = { ... };
service.getDetails(request, detailsCallback);
}
}
function detailsCallback(place, status) {
// add place marker to maps and assign info window and info window event
}
With a small modification of your code, it can be achieved.
var total = 1337; // Some number
var internal_counter = 0;
var fn_callback = function() {
searchCallback.apply(this, arguments);
if (++internal_counter === total) {
BUILD();
}
};
for (var i=0; i<total; i++) {
service.search(request, fn_callback);
...
Explanation
First, we create a local function and variable.
The variable is a counter, which is increased when the callback is called.
The function is passed to the asynchronous method (service.search), which calls the original callback. After increasing the counter, check the value of the counter against the variable which holds the total number of iterations. If these are equal, call the finishing function (BUILD).
A complex case: Dealing with nested callbacks.
var types = { '...' : ' ... ' };
function search() {
var keys = Object.keys(types);
var total = keys.length;
// This counter keeps track of the number of completely finished callbacks
// (search_callback has run AND all of its details_callbacks has run)
var internal_counter = 0;
for (var i=0; i<total; i++) {
var request = { '...' : ' ... ' };
services.search(request, fn_searchCallback);
}
// LOCAL Function declaration (which references `internal_counter`)
function fn_searchCallback(results, status) {
// Create a local counter for the callbacks
// I'm showing another way of using a counter: The opposite way
// Instead of counting the # of finished callbacks, count the number
// of *pending* processes. When this counter reaches zero, we're done.
var local_counter = results.length;
for (var i=0; i<results.length; i++) {
service.getDetails(request, fn_detailsCallback);
}
// Another LOCAL function (which references `local_counter`)
function fn_detailsCallback(result, status) {
// Run the function logic of detailsCallback (from the question)
// " ... add place marker to maps and assign info window ... "
// Reduce the counter of pending detailsCallback calls.
// If it's zero, all detailsCallbacks has run.
if (--local_counter === 0) {
// Increase the "completely finished" counter
// and check if we're finished.
if (++internal_counter === total) {
BUILD();
}
}
} // end of fn_detailsCallback
} // end of fn_searchCallback
}
The function logic is explained in the comments. I prefixed the heading of this section with "Complex", because the function makes use of nested local functions and variables. A visual explanation:
var types, BUILD;
function search
var keys, total, internal_counter, fn_searchCallback;
function fn_searchCallback
var result, status; // Declared in the formal arguments
var local_counter, i, fn_detailsCallback;
function fn_detailsCallback
var result, status; // Declared in the formal arguments
In the previous picture, each indention level means a new scope Explanaation on MDN.
When a function is called, say, 42 times, then 42 new local scopes are created, which share the same parent scope. Within a scope, declared variables are not visible to the parent scope. Though variables in the parent scope can be read and updated by variables in the "child" scope, provided that you don't declare a variable with the same name. This feature is used in my answer's function.
I think you understand this already, but as it is the BUILD() is getting called linearly while the previous callback functions are still running. It's like you've created extra threads. One way to solve the problem would be to make BUILD a callback from the search function with the for loop in it. This would guarantee all functionality is complete before calling it.
This question might help implement the callback: Create a custom callback in JavaScript
I am looking at a javascript code that manipulates an HTML A tag , and I'm having trouble understanding how it sets up the "onclick" property. It seems to be telling it to update ytplayer_playitem with the index variable j and then call ytplayer_playlazy(1000)
But what's up with all the parentheses? What details in the javascript syntax allows it to be setup like this?
var a = document.createElement("a");
a.href = "#ytplayer";
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
Well, basically, the value of onclick is a function that will get called when the element is clicked. Whatever you want to happen when the user clicks the element goes in the body of the function.
You could create a named function and then assign it to the element's onclick attribute:
function youClickedMe() {
...
}
a.onclick = youClickedMe
but that clutters up the namespace with a function name that is never referenced anywhere else. It's cleaner to create an anonymous function right where you need it. Normally, that would look like this:
a.onclick = function() { ... }
But if we try that with your specific example:
a.onclick = function() {
ytplayer_playitem = something; // ??
ytplayer_playlazy(1000);
}
We see that it hard-codes the something that gets played. I'm assuming the original code was taken from a loop which generates several clickable links to play; with the code just above, all of those links would play the same thing, which is probably not what you want.
So far, so straightforward, but this next leap is where it gets tricky. The solution seems obvious: if you're in a loop, why not just use the loop variable inside the function body?
// THIS DOESN'T WORK
a.onclick = function() {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
}
That looks like it should work, but unfortunately the i inside the function refers to the value of the variable i when the function is called, not when it's created. By the time the user clicks on the link, the loop that created all the links will be done and i will have its final value - probably either the last item in the list or one greater than that item's index, depending on how the loop is written. Whatever its value is, you once again have the situation where all links play the same item.
The solution in your code gets a little meta, by using a function whose return value is another function. If you pass the loop control variable to the generating function as an argument, the new function it creates can reference that parameter and always get the value that was originally passed in, no matter what has happened to the value of the outer argument variable since:
function generate_onclick(j) {
// no matter when the returned function is called, its "j" will be
// the value passed into this call of "generate_onclick"
return function() { ytplayer_playitem = j; ytplayer_playlazy(1000); }
}
To use that, call it inside the loop like this:
a.onclick = generate_onclick(i);
Each generated function gets its very own j variable, which keeps its value forever instead of changing when i does. So each link plays the right thing; mission accomplished!
That's exactly what your posted original code is doing, with one small difference: just like the first step in my explanation, the author chose to use an anonymous function instead of defining a named one. The other difference here is that they are also calling that anonymous function immediately after defining it. This code:
a.onclick = (function (j) { ... })(i)
is the anonymous version of this code:
function gen(j) { ... }
a.onclick = gen(i)
The extra parens around the anonymous version are needed because of JavaScript's semicolon-insertion rules; function (y) {...}(blah) compiles as a standalone function definition followed by a standalone expression in parentheses, rather than a function call.
"But what's up with all the parentheses? "
Most of the parentheses are just doing what you'd expect.
There's an extra set that isn't technically needed, but is often used as a hint that the function is being invoked.
// v-v---these are part of the function definition like normal
a.onclick = (function (j) {
// ^-----------this and...v
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
// v---...this are technically not needed here, but are used as a hint
})(i);
// ^-^---these invoked the function like normal
"What details in the javascript syntax allows it to be setup like this?"
The upshot is that the function is invoked immediately, and passed i so that its value is referenced by the j parameter in the immediately invoked function.
This creates a variable scope that the returned function will continue to have access to. This way it always has access to the j variable, and not the i that gets overwritten in the loop.
These inlined functions are abused a bit IMO. It becomes clearer if you simply make it a named function.
for(var i = 0; i < 10; i++) {
// create the new element
a.onclick = createHandler(i);
// append it somewhere
}
function createHandler (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
}
The resulting handler is exactly the same, but the code is much less cryptic.
Right, I'm going to guess that the surrounding code looks like this:
for (var i = 0; i < playitems.length; i++) {
// above code here
}
Now, you could do the obvious thing here, and assign the onclick property like this:
a.onclick = function() {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
};
However that wouldn't work very well, because the value of i changes. Whichever link was clicked, the last one would be the one activated, because the value of i at that point would be the last one in the list.
So you need to prevent this happening. You need to do this by creating a new scope, which is done by creating an extra function, which is immediately invoked:
(function (j) {
// some code here
})(i);
Because i has been passed into the function, the value is passed rather than a reference to the variable being kept. This means that you can now define a function which will have a reference to the correct value. So you get your extra function to return the click handling function:
a.onclick = (function (j) { // j is the right number and always will be
return function () { // this function is the click handler
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
So each a element has its own click handler function, each of which has its own individual j variable, which is the correct number. So the links, when clicked, will perform the function you want them to.
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
This creates a "closure" to ensure that the value of i that is bound to the handler is the value of i "at that time" and not i in the general.
In your code, the function inside the () is an expression, executed and passed the variable i. This is the (i) you see in the end part. In this executed function expression, the i becomes the local variable j. This executed function expression returns the handler function that is to be bound the onclick event carrying the value of j which was i "at that time"
if i did not use the closure:
//suppose i is 1
var i = 1;
a.onclick = function () {
ytplayer_playitem = i;
ytplayer_playlazy(1000);
};
//and changed i
i = 2;
//if you clicked the <a>, it would not be 1 onclick but 2 because you
//did not assign the value of i "at that time". i is "tangible" this way
a.onclick = (function (j) {
return function () {
ytplayer_playitem = j;
ytplayer_playlazy(1000);
};
})(i);
What you have here is a self-invoking anonymous function. Let's break it down, first replacing the body of the function with something simpler (return j + 1;):
function( j ) { return j + 1; }
This s a run-of-the-mill anonymous function or closure. This line of code is an expression, and so it has a value, and that value is a function. Now we could do this:
var foo = function( j ) { return j + 1; }
foo( 5 ); // => 6
You recognize this, I'm sure—we're assigning the anonymous function to the variable foo, and then calling the function by name with the argument i. But, instead of creating a new variable, because the closure is an expression we can call it like this instead:
( function( j ) { return j + 1; } )( 5 ); // => 6
Same result. Now, it's just returning j + 1 but in your code it returns something else: Another anonymous function:
return function() { /* ... */ }
What happens when we have a self-invoking anonymous function that returns a function? The result is the "inner" function that was returned:
a.onclick = ( function( j ) {
return function() {
ytplayer_playitem = j;
ytplayer_playlazy( 1000 );
}
}
)( i );
If i was equal to 9 then a.onclick would now hold a function equivalent to this:
function() {
ytplayer_playitem = 9;
ytplayer_playlazy( 1000 );
}
As others have pointed out, the usefulness of this is that when ( function( j ) { /* ... */ } )( i ) is invoked you are capturing the value of i at that time and putting it into j rather than creating a reference to the value i holds, which may (and probably will) change later on.