In the following code, it should store 26 different variables under the object GameThumbs for each of the 26 objects in r.Showcase
However, all that is stored is Object {26: "http://t3.rbxcdn.com/1e2476473494bfb202592501a5f86655"}
I don't see anything wrong with my code, please help me fix it. Thank you (sorry if I am not describing the problem well enough; my English is not very well)
function info(id) {
var User = {};
var GameIds = {};
var GameNames = {};
var GameThumbs = {};
$("html").html("");
$.get('http://api.roblox.com/Users/'+id, function(r) {
User['Username'] = r.Username;
}).done(function() {
$.get('http://www.roblox.com/Contests/Handlers/Showcases.ashx?userId='+id, function(r) {
for (var x in r.Showcase) {
GameIds[x] = r.Showcase[x]['ID'];
GameNames[x] = r.Showcase[x]['Name'];
var link = 'http://www.roblox.com/item-thumbnails?params=%5B%7BassetId:'+GameIds[x]+'%7D%5D';
$.get(link, function(d) {
GameThumbs[x] = d[0]['thumbnailUrl'];
});
}
}).done(function() {
console.log(User, GameIds, GameNames, GameThumbs);
});
});
}
Requested sample JSON (of link)
[{"id":45778683,"name":"Temple of the Ninja Masters!","url":"/Temple-of-the-Ninja-Masters-place?id=45778683","thumbnailFinal":true,"thumbnailUrl":"http://t0.rbxcdn.com/6eb2d1f05d120757ae1e72d8e28c918e","bcOverlayUrl":null,"limitedOverlayUrl":null,"deadlineOverlayUrl":null,"limitedAltText":null,"newOverlayUrl":null,"imageSize":"large","saleOverlayUrl":null,"iosOverlayUrl":null,"transparentBackground":false}]
of /Contets/Handlers/Showcases.ashx...
{"Error" :"", "Count" :"43", "UserIsAtMaxShowcases":"false", "Showcase" : [{"ID":164081122,"Name":"For ShrekIetsky"}, {"ID":163393918,"Name":"N7"}, {"ID":162493258,"Name":"Hackathon 2014"}, {"ID":158445333,"Name":"bests kings"}, {"ID":157560077,"Name":"Don't be an idiot."}, {"ID":157183481,"Name":"Lots of Thumbnails"}, {"ID":156339175,"Name":"Watch out!"}, {"ID":155669451,"Name":"YES I AM BIASED"}, {"ID":155669434,"Name":"Place_4"}, {"ID":155669424,"Name":"Place_3"}, {"ID":155669403,"Name":"Place_2"}, {"ID":155669381,"Name":"Place_1"}, {"ID":153242188,"Name":"earth2noah"}, {"ID":152827982,"Name":"Buying Egg Merch?"}, {"ID":152563337,"Name":"Inside Mallowpuff's head"}, {"ID":151207481,"Name":"nothin to see here"}, {"ID":151117405,"Name":"Acronym test"}, {"ID":149734880,"Name":"TESTING"}, {"ID":146087560,"Name":"FPS maps"}, {"ID":144140735,"Name":"Old Memories"}, {"ID":144035150,"Name":"Quitting"}, {"ID":142668569,"Name":"Challange Projecto"}, {"ID":140204830,"Name":"Element Obby! [Beta]"}, {"ID":140201014,"Name":"NEVERMIND KIDS C:"}, {"ID":138010986,"Name":"Passage of the Mind"}, {"ID":138006022,"Name":"I see you"}, {"ID":135852345,"Name":"Trade Information!"}, {"ID":135303493,"Name":"Future project"}, {"ID":129885802,"Name":"ololwoaplsiaeo"}, {"ID":129768085,"Name":"SiIencedSong's Place Number: 16"}, {"ID":127537299,"Name":"olopio"}, {"ID":120338485,"Name":"LmaD building!"}, {"ID":120338432,"Name":"Maze Daze [INACTIVE]"}, {"ID":120338402,"Name":"Volcano Survival maps"}, {"ID":120338370,"Name":"[[OpenSource]]"}, {"ID":120338338,"Name":"Yes"}, {"ID":120338296,"Name":"No"}, {"ID":120338264,"Name":"hackathon0n stuff"}, {"ID":118933268,"Name":"-"}, {"ID":118932768,"Name":"Testing"}, {"ID":118573473,"Name":"ROBLOX fame HQ."}, {"ID":118324272,"Name":"Simple spawnroom"}, {"ID":117834273,"Name":"Lightningsurvivor's fort."}] }
It's the same old problem with asynchronous code.
When you iterate with for...in, x changes. When the callback function of $.get is called for the first time, for has completed its iterations and x now yields the last value of the iterations, which is presumably 26.
You have two solutions for this:
make the $.get call synchronous (bad, never do this);
create a closure for the iteration.
Using jQuery, for example:
$.each(r.Showcase, function(x) {
...
$.get(link, function(d) {
GameThumbs[x] = d[0]['thumbnailUrl'];
});
});
instead of for (var x in r.Showcase). This way, x is a local variable inside the callback function of $.each, and its value will be the same when returning from $.get.
Related
What am I doing wrong, and how can one pass variables to a different function within the same wrapping variable/function.
Example:
function customFunctionWrap(){
this.myVar1 = 0;
this.getCurrentPosition = function(){
if (navigation.geolocation) {
navigator.geolocation.getCurrentPosition(function(position){});
}
},
this.doSomething = function(){ // Works
//Do something, return
this.callWithParams(); //Works
},
//If I remove passing in 'value1',calling it elsewhere works
this.doSomethingWithParams = function(value1){
//Use value1
//Return
},
this.callWithParams = function(){
var value1 = 'xyz'; //Is a variable that changes based on some DOM element values and is a dynamic DOM element
this.doSomethingWithParams(value1); //THROWS TYPEDEF ERROR: this.doSomethingWithParams is not a function
this.getCurrentPosition();
}
};
var local = new customFunctionWrap();
local.doSomething(); //WORKS
I know there is another way to do it and then directly use customFunctionWrap.callWithParams(), but am trying to understand why the former approach is erroring out.
var customFunctionWrap = {
myVar1 : 0,
callWithParams : function(){
}
}
What JS sees:
var customFunctionWrap = (some function)()
returned function is fired, because the last (), so it has to yield/return something, otherwise, like in your code it is "returning" undefined.
So your given code does not work.
The very first fix is to delete last 2 characters from
var customFunctionWrap = (some function)()
to make it return constructor.
I am in a bit of a bind here regarding a code I have to write.
At the moment I am trying to iterate through an array of strings. After a certain text is found, if a value is true, the value is bound to a temporaryObject.img. After that a call has to be made to the server in order for some information to be retrieved and to be bound to the same temporaryObject as a second property (temporaryObject.url). Unfortunately, the call is resolved probably after the iteration is done because it does not add this add the url from the call to the server in the Object. The libraries I am using are angularjs and jquery.
Here is a part of the code I wrote.
$scope.Data = [];
var strArray = ["img1", "link", "img2", "link2", "img3", "link3"];
var re1 = /<img/;
for(var i = 0 ; i < strArray.length ; i++) {
var tempObject = {};
var test = re1.test(strArray[i]);
if (test){
tempObject.img = strArray[i + 1];
myAngularService.getServerInformation().then(function(result){
tempObject.url = result;
$scope.Data.push(tempObject);
}
}
Unfortunately, the information from the server does not arrive in the same time as the iteration is done or something goes wrong since $scope.Data will not contain the url requested from the server. Any suggestions as how to use maybe a promise for this kind of code would be appreciated. I tried searching every possible solution but with no avail.
Javascript is function-scoped. In other words:
if (true) {
var testing = 5;
}
console.log(testing); // Logs 5
Applying this to your example, tempObject in your .then callback function are all referencing the same variable!
One solution is to use an Immediately-Invoked Function Expression (IIFE) inside your for statement to create a new scope:
for(var i = 0 ; i < strArray.length ; i++) {
(function() {
var tempObject = {}; // Is now declared within the new function
/* ... code here ... */
})();
}
Another solution is to simply use $.each (or angular.forEach):
$.each(strArray, function(index, value) {
var tempObject = {}; // Is now declared within the new function
/* ... code here ... */
});
Regarding waiting for all your calls to finish, one solution is to store your promises in an array use angular's $q service:
$scope.Data = [];
var promises = [];
var strArray = ["img1", "link", "img2", "link2", "img3", "link3"];
var re1 = /img/;
$.each(strArray, function (index, value) {
var tempObject = {};
var test = re1.test(value);
if (test) {
tempObject.img = strArray[index + 1];
var myPromise = myAngularService.getServerInformation();
myPromise.then(function (result) {
tempObject.url = result;
$scope.Data.push(tempObject);
}
promises.push(myPromise);
}
})
$q.all(promises).then(function() {
// All calls will have finished
console.log($scope.Data);
}, function() {
// At least 1 call failed!
console.log("an error occurred!");
});
I am working with the AddThis JavaScript API. The method I am struggling with is documented here:
http://support.addthis.com/customer/portal/articles/1137944-getting-counter-values-dynamically
I can obtain the data I require from the "obj" object - but only within that method. I can't seem to return the data to a global variable that I can use in my jQuery loop. The problem is I have a limited understanding of Javascript objects. Here is my code:
addthis.addEventListener('addthis.ready', addthisReady);
function addthisReady() {
var myobj = {};
addthis.sharecounters.getShareCounts({service: ['facebook', 'twitter', 'pinterest'], countUrl: 'http://www.addthis.com/'}, function(obj) {
console.log(obj); // OK
myobj = obj;
});
console.log(myobj); // Not OK
}
My ultimate goal is to have multiple article links on a page and then use jQuery and this API method to append the total share counts to their linked titles. EG;
Article Link X (22 Shares)
Article Link Y (13 Shares)
Article Link Z (13 Shares)
Any help would be grand.
CJ
PROGRESS UPDATE - Almost there...
The code below factors in your advice and an example provide by the API vendors. It is almost there, but the callback randomly updates only one of the elements in the Each loop.
The example code - commented out - indicated that multiple calls to the method should be possible.
Here is the code:
$(document).ready(function(){
addthis.addEventListener('addthis.ready', addthisReady);
function addthisReady() {
/*
addthis.sharecounters.getShareCounts('facebook', function(obj) {
document.getElementById('basic-example').innerHTML = '<code>'+JSON.stringify(obj, undefined, 4)+'</code>';
});
addthis.sharecounters.getShareCounts(['facebook', 'twitter', 'pinterest'], function(obj) {
document.getElementById('multiple-services').innerHTML = '<code>'+JSON.stringify(obj, undefined, 4)+'</code>';
});
addthis.sharecounters.getShareCounts({service: 'facebook', countUrl: 'http://www.addthis.com/'}, function(obj) {
document.getElementById('specific-url').innerHTML = '<code>'+JSON.stringify(obj, undefined, 4)+'</code>';
});
addthis.sharecounters.getShareCounts({service: ['facebook','twitter'], countUrl: 'http://www.addthis.com/'}, function(obj) {
document.getElementById('specific-url-multiple-services').innerHTML = '<code>'+JSON.stringify(obj, undefined, 4)+'</code>';
});
*/
$('.ico-shares').each(function(index) {
var elem = this;
var share_url = $(elem).attr('href').split('#')[0];
var shareLabel = $(elem).text();
var total_count = 0;
//Request Counts
addthis.sharecounters.getShareCounts({service: ['facebook', 'twitter'], countUrl: share_url}, function(obj) {
for (var key in obj)
{
total_count += obj[key].count;
}
if (total_count > 0)
{
shareLabel = total_count + ' Share';
}
if (total_count > 1)
{
shareLabel = total_count + ' Shares';
}
alert(shareLabel);
$(elem).text(shareLabel);
});
});
}
});
My URL is here: http://banarra.cjweb.com.au/html/news_article.php
Note there are 3 x "More News" articles at the bottom, but only one has it's Share link updated.
Thanks very much for your time.
CJ
The code you've posted isn't going to be the problem.
If you sent your request, and then waited a few seconds, and then checked the value of the variable using the console, you should see it set to the value you were expecting.
The problem is asynchronicity.
See, when you send a request off to the server (ie: when that function actually fires), it doesn't sit there and wait for it to come back, before moving to your next line of code.
So if you have code that looks like:
var myObj = {};
getAsyncData(function myCallback (o) { myObj = o; });
console.log(myObj);
What's actually going to happen is that function is going to fire, and your code is going to keep on running.
The server hasn't returned your data, so myObj === {}.
Some time later, the server will return your data, and run that function that you gave it.
So if you come back in some time:
setTimeout(function () { console.log(myObj); }, 10000); // 10 sec
Then you should see it's being set.
The trick here is that you actually need to trigger stuff in your async code.
The rest of your program doesn't know anything is changed or anything is new, and it certainly hasn't sat and waited for it.
var myObj = {};
getAsyncData(function (o) { myObj = o; doStuff(myObj); });
var doStuff = function (obj) { console.log("Back from the server"); console.log(obj); };
To that end, I'd suggest looking at the link in the comments.
EDIT
To better grasp the problem (not the many solutions), imagine your async calls used a setTimeout to fire your function and pass in the data.
You wouldn't expect your whole JS app to sit and wait for one change in a setTimeout.
var data_is_set = false,
get_data = function (callback) {
var data = true,
time_to_wait = 3000; // 3sec
setTimeout(function () { callback(data); }, time_to_wait);
};
get_data(function (val) { data_is_set = val; });
console.log(data_is_set); // false
The rest of the program is going to run, until that time goes by, and then your function gets called.
Nobody else knows what happened, so you need to make functions which now trigger updates, that you can call inside of your callback.
Can you just return obj?
var myobj = addthis.sharecounters.getShareCounts({service: ['facebook', 'twitter', 'pinterest'], countUrl: 'http://www.addthis.com/'}, function(obj) {
console.log(obj); // OK
return obj;
});
I'm working on some script for a set of functions that all operate from one call and take a large number of parameters to return one value. The main function requires the use of 11 other functions which need to work with the same parameters. I have it structured somewhat like this:
function mainfunction(param1, param2, ..., param16)
{
//do a bunch of stuff with the parameters
return output;
}
function secondaryfunction1()
{
//gets called by mainfunction
//does a bunch of stuff with the parameters from mainfunction
}
Is there anything I can do to make the parameters passed to mainfunction available to all the secondary functions without passing them or making them global variables? If not, that's fine, I'll pass them as parameters - I'm curious as to whether or not I can do it more elegantly.
You can place the definition of secondaryfunction1 inside mainfunction:
function mainfunction(param1, param2, ..., param16){
function secondaryfunction1() {
// use param1, param2, ..., param16
}
secondaryfunction1();
}
Update:
As #dystroy pointed out, this is viable if you don't need to call secondaryfunction1 somewhere else. Where the list of parameters would be coming from in this case - I don't know.
You could use arguments to pass to secondaryFunction1 all the arguments of mainfunction. But that would be silly.
What you should probably do, and what is usually done, is embed all the parameters in an "options" object :
function mainfunction(options){
secondaryfunction1(options);
}
function secondaryfunction1(options) {
// use options.param1, etc.
}
// let's call it
mainfunction({param1: 0, param2: "yes?"});
This leds to other advantages, like
naming the parameters you pass, it's not a good thing for maintenance to have to count the parameters to know which one to change. No sane library would let you pass 16 parameters as direct unnamed arguments to a function
enabling you to pass only the needed parameters (the other ones being default)
#Igor 's answer (or some variation) is the way to go. If you have to use the functions elsewhere, though (as #dystroy pointed out), then there is another possibility. Combine your parameters together into an object, and pass that object to the secondary functions.
function combineEm() {
// Get all parameters into an array.
var args = [].slice.call(arguments, 0),
output = {},
i;
// Now put them in an object
for (i = 0; i < args.length; i++) {
output["param" + i] = args[i];
}
return output;
}
From your main function, you can do:
function mainfunction(param1, param2, ..., param16) {
var params = combineEm(param1, param2, ..., param16);
var output = secondaryfunction(params);
// etc.
return output;
}
Edit: I just wanted to clarify that all of the proposed suggestions so far do work. They just each have their own trade-offs/benefits.
I tried just suggesting some changes to other answers, but ultimately I felt like I needed to just post my solution to this.
var externalFn = function(options) {
var str = options.str || 'hello world';
alert(str);
};
var main = function(options) {
var privateMethod = function() {
var str = options.str || "foobar";
alert("str: " + str);
};
// Bind a private version of an external function
var privateMethodFromExternal = externalFn.bind(this, options);
privateMethod();
privateMethodFromExternal();
};
main({ str: "abc123"});
// alerts 'str: abc123'
// alerts 'abc123'
main({});
// alerts 'str: foobar'
// alerts 'hello world'
It seems like the main point of the question is that the functions used by the 'main function' shouldn't have to keep having the options/context passed to them.
This example shows how you can use privateMethods inside the function
It also shows how you can take external functions (that you presumably use outside of main) and bind a private method version of them for use inside main.
I prefer using some sort of 'options' object, but that aspect isn't really that important to the question of scoping that the OP was really asking about. You could use 'regular' parameters as well.
This example can be found on codepen.
Here's an incredibly naughty solution, if you're interested in that sort of thing.
var f1 = function() {
var a = 1;
var _f2 = f2.toString().replace(/^function[^{}]+{/, '');
_f2 = _f2.substr(0, _f2.length - 2);
eval(_f2);
}
var f2 = function(a) {
var a = a || 0;
console.log(a);
}
f2(); // logs 0
f1(); // logs 1
It executes the contents of some external function entirely in the current scope.
However, this sort of trickery is almost definitely an indicator that your project is mis-organized. Calling external functions should usually be no more difficult than passing an object around, as dystroy's answer suggests, defining the function in-scope, as Igor's answer suggests, or by attaching some external function to this and writing your functions primarily against the properties of this. Like so:
var FunLib = {
a : 0,
do : function() {
console.log(this.a);
}
}
var Class = function() {
this.a = 1;
this.do = FunLib.do;
this.somethingThatDependsOnDo = function() {
this.a++;
this.do();
}
}
var o = new Class();
FunLib.do() // 0
o.do() // 1
o.somethingThatDependsOnDo(); // 2
o.do() // 2 now
Similarly, and possibly better-solved with a class hierarchy.
function BasicShoe {
this.steps_taken = 0;
this.max_steps = 100000;
this.doStep = function() {
this.steps_taken++;
if (this.steps_taken > this.max_steps) {
throw new Exception("Broken Shoe!");
}
}
}
function Boot {
this.max_steps = 150000;
this.kick_step_equivalent = 10;
this.doKick = function() {
for (var i = 0; i < this.kick_step_equivalent; i++) {
this.doStep();
}
}
}
Boot.prototype = new BasicShoe();
function SteelTippedBoot {
this.max_steps = 175000;
this.kick_step_equivalent = 0;
}
SteelTippedBoot.prototype = new Boot();
I am struggling to understand how this JavaScript code work. I am learning JS, and not exposed to a dynamic, functional language before. So, I visualize function calls in bit procedural, hierarchical order. With d3.js, one can draw svg elements as explained here
var dataset = [ 5, 10, 15, 20, 25 ];
d3.select("body").selectAll("p")
.data(dataset)
.enter()
.append("p")
.text("New paragraph!");
Let’s change the last line:
.text(function(d) { return d; });
Check out what the new code does on this demo page.
Whoa! We used our data to populate the contents of each paragraph, all thanks to the magic of the data() method. You see, when chaining methods together, anytime after you call data(), you can create an anonymous function that accepts d as input. The magical data() method ensures that d is set to the corresponding value in your original data set, given the current element at hand.
This magic, mentioned above is what I fail to understand. "d" is not a global variable, as if I change to another (c) name, it still works. So, the data method may be setting the value for the anonymous fn.
But, typically(with my limited reading) chaining is possible because the current function returns an object, on which the next method can be invoked. In the above case, how data method knows whether a text ("New paragraph!") is passed by the user, otherwise pass the data to the anonymous fn. The doubt is, the text method is down the line and data() is already executed. How the data is passed to the anonymous function?
thanks.
Digging into d3.js internals shows the following result for text function:
d3_selectionPrototype.text = function(value) {
return arguments.length < 1
? this.node().textContent : this.each(typeof value === "function"
? function() { var v = value.apply(this, arguments); this.textContent = v == null ? "" : v; } : value == null
? function() { this.textContent = ""; }
: function() { this.textContent = value; });
};
In case the supplied argument is a function, the following code gets executed:
this.each(function() {
var v = value.apply(this, arguments); // executing function provided
this.textContent = v == null ? "" : v;
});
Function each is declared as:
d3_selectionPrototype.each = function(callback) {
for (var j = -1, m = this.length; ++j < m;) {
for (var group = this[j], i = -1, n = group.length; ++i < n;) {
var node = group[i];
if (node) callback.call(node, node.__data__, i, j); // this is the line you are interested in
}
}
return this;
};
so on each invocation it supplies an element from this. And, getting down to it, this is populated by data function invocation.
Well, I have never used d3 before, but this is what I understand.
d is the data object (I would call it data instead of d had set in the data() method.
So what does the text() method does? Will it will call the function and use it's output, something like this:
function text (callback) {
var theText;
if (typeof callback === "function") {
theText = callback(dataset);
} else {
theText = callback;
}
// does something more
}
So, if callback is a function call it, and use its return value as text.
Then, what I'm guessing, is that if the function is an array, it will call the text method for each element in the array.
Something like this...
function text(callback) {
var theText;
if (typeof callback === "function") {
theText = callback(dataset);
} else {
theText = callback;
}
if (theText instanceof Array) { // this is not the best way to check if an object is an array, I'll come back to this later. I'm sorry.
for (var i=0, len=theText.length; i<len; i++) {
text(theText[i]);
}
} else {
// do something else
}
// do something more
}
please take into account that this would be a really simple version of what really happens.
If it's not clear enough please let me know.