I have a JSON file that contains the following:
[{name:joe,number:4},{name:ralph,number:76}]
Simply put, I'm trying to create a function that returns that as an array:
function createArray(){
x = $.getJSON("/names.json", function(json) {
var myArray = [];
$.each(json, function() {
myArray.push(json);
});
return myArray;
});
return x;
}
This is not working, I'm not sure what part of it doesnt make sense
You are doing an asynchronous request. $.getJSON does not return the result of your callback.
You need to pass a callback into your createArray function. Also, if your JSON data is already an array, you don't need to process the response data.
function createArray(cb){
$.getJSON("/names.json", cb);
}
So for example you'd change this:
var myArray = createArray();
// Process myArray
into this:
createArray(function(myArray) {
// Process myArray
});
The problem is that you can't just assign the result of a network request. When you call getJSON, you start a network request, but after the request starts, the JS will keep running other stuff. The get the result of an asynchronous request, you need to use a callback that will process that data and do something with it.
Like loganfsmyth said, you're not taking into account that $.getJSON is asynchronous, and it's not going to return what you think it is (it actually returns a jqXHR Object. Write your code to handle the asynchronous nature using closures:
function createArray(cb){
$.getJSON("/names.json", function(json) {
var myArray = [];
$.each(json, function() {
myArray.push(json);
});
cb(myArray);
});
}
createArray(function(array) {
//your array is available here
console.log(array);
}
Your keys and values should be Strings
[{"name":"joe", "number":4},{"name":"ralph", "number":76}]
and this
$.each(json, function() {
myArray.push(json);
});
should become
$.each(json, function(key, val) {
if(myArray[key] == undefined)
{ myArray[key] = new Array();
}
myArray[key].push(val);
});
What you get from 'json' is an array.
try this,
var ar = [{
"name": "joe",
"number": "4"},
{
"name": "ralph",
"number": "76" }];
alert(ar[0].name);
This would alert the name "joe".
ar[0] is the first element of the array and in your case this is an object with properties name and number. You can get/set them in normal way.
var myArray = [];
$.getJSON("/names.json", function(json) {
myarray=json;
});
NowmyArray is an array with your Objects.
You can't do that, your function will return a promise object, but never the json response itself. The response will be available from the promise only after the request is complete (which is not immediately after function return because the request is asynchronous).
Why don't you just use the json from the callback? I mean this:
function createArray(){
$.getJSON("/names.json", function(json) {
// Whatever you need to do with your json,
// do it here. This code will run after
// createArray returns. And you can't
// return anything from here, it's useless.
// Actually, you don't even need the outer
// createArray function.
});
}
Related
I have a list of items being stored in an array. For each item in the array, I need to make an API request and add some of the data that comes back into another array so that I can perform operations on it later.
I think the issue is that my get request is asynchronous, and as a result the data is not necessarily loaded when I am trying to add it to the array, but I thought that's what .then was supposed to cover.
var cardRequestList = ["Scapeshift","Ghostform", "Daybreak Chaplain"]
var url = "https://api.magicthegathering.io/v1/cards?name=%22";
var cardInfo =[];
for (var cardName in cardRequestList){
var results = getCard(cardRequestList[cardName]);
cardInfo.push(results);
}
console.log(cardInfo); // results should come back here
function getCard(cardName){
var cardUrl = url.concat(cardName,"%22");
$.get(cardUrl).then(
function(data) {
var temp = [data.cards[0].name,data.cards[0].printings]
return temp;
}, function() {
alert( "$.get failed!" );
}
);
}
then() only works for the specific request it's invoked on.
To solve the issue you have you could make your requests in a loop, adding the jqXHR object returned from those calls to an array which you can then apply to $.when() to execute some other logic once all the requests have completed.
Within the requests themselves you need to add the returned data to an array as you cannot return anything from an async call.
With all that said your code would look something like this:
var cardRequestList = ["Scapeshift", "Ghostform", "Daybreak Chaplain"]
var url = "https://api.magicthegathering.io/v1/cards?name=%22";
var cardInfo = [];
var requests = cardRequestList.map(function(cardName) {
return getCard(cardName);
});
function getCard(cardName) {
var cardUrl = url.concat(cardName, "%22");
return $.get(cardUrl).then(function(data) {
cardInfo.push([data.cards[0].name, data.cards[0].printings]);
}, function() {
alert("$.get failed!");
});
}
$.when.apply($, requests).done(function() {
// all requests finished and cardInfo has been populated, place logic here...
console.log(cardInfo);
});
function getHiddenTable(){
var url = ".......";
var apptoken = "blahblah";
return $.get(url, {
act: "API_DoQuery",
clist: "7.8.6",
data: apptoken
});
}
function getRprTable(){
var url = "........";
var apptoken = "blahblah";
return $.get(url, {
act: "API_DoQuery",
clist: "3.60",
data: apptoken
});
}
function main(){
getHiddenTable().then(function(xml_hidden){
hiddenTable = xml_hidden;
console.dirxml(hiddenTable);
return getRprTable();
}).then(function(xml_rpr){
rprTable = xml_rpr;
console.dirxml(rprTable);
});
}
main();
getHiddenTable() returns a $.get request and getRprTable() also returns a $.get request
Both of these functions return two separate xml files that contain different data. I assign both xml files to two separate global variables so they have scope to other function that I need to use them in. However, both console statements display the initial request from getHiddenTable(). But why? And what can I do to fix this?
Update
Hey guys, thanks for the help. I found out my problem with much frustration and actually utilized $.when, from the person who provided the idea in the comments.
$.when(getHiddenTable(), getRprTable()).done(function(xml_hidden, xml_rpr){
console.dirxml(xml_hidden[0]);
console.dirxml(xml_rpr[0]);
});
Accessing the data value of the callback function objects can be done by simply accessing the 0th index.
Thanks
You'll likely want to use Promise.all() to run multiple promises at once and return the results of all the promises once they have all finished.
Example:
Promise.all([getHiddenTable(), getRprTable()])
.then(function(results) {
let hiddenTable = results[0];
let rprTable = results[1];
// do stuff with your results
});
Hope that helps
Is there a way to store data to a variable?
I tried:
$scope.another =
function(){
var new_data;
userService.getInfo().success(function(data){
new_data = data;
});
return new_data;
};
var data = $scope.another();
but it returns 'undefined' in the console log. Thank you
EDIT
I now get an empty array for new_data .
var new_data = [];
$scope.another =
function(callback){
userService.getInfo().success(function(data){
paymentService.getCashierParams({ "cardNumber": data.cardNumber}).success(function(data){
gameService.getAllgames({ "PID":data.GetCashierParameters.PID, "limit": 6, "skinID": 1}).success(function(data) {
callback(data.data.GetFlashGamesResult.Data.FlashGame);
});
});
});
};
$scope.another(function(result){
new_data = result;
});
console.log(new_data);
You need to think about this problem differently. Your getInfo method returns a promise. A promise's success callback is never immediately called. It may be called sometime in the future, but in the meantime your code will continue executing and the return value of $scope.another will be undefined.
Instead, place whatever logic you wish to execute within the success callback.
userService.getInfo().success(function (data) {
// Do stuff with data.
});
If you are not used to working with asynchronous data, this may seem weird. But it is much better than the alternative, which is hanging the page for potentially many seconds while a network request, database read, etc, completes.
If you are worried about indentation, you can create separate function(s) to handle the data.
function processData(data) {
// Process stuff...
return data;
}
function handleData(data) {
data = processData(data);
console.log(data);
}
userService.getInfo().success(handleData);
This is due to the asynchronous function that you called. Try to use callback instead. Something like this:
$scope.another =
function(fn){
userService.getInfo().success(function(data){
fn(data);
});
};
var data = $scope.another(function(doSomething) {
alert(doSomething);
return doSomething;
};
I need to call an async function (with loop) for multiple values and wait for those results. Right now I'm using the following code:
(function(){
var when_done = function(r){ alert("Completed. Sum of lengths is: [" + r + "]"); }; // call when ready
var datain = ['google','facebook','youtube','twitter']; // the data to be parsed
var response = {pending:0, fordone:false, data:0}; // control object, "data" holds summed response lengths
response.cb = function(){
// if there are pending requests, or the loop isn't ready yet do nothing
if(response.pending||!response.fordone) return;
// otherwise alert.
return when_done.call(null,response.data);
}
for(var i=0; i<datain; i++)(function(i){
response.pending++; // increment pending requests count
$.ajax({url:'http://www.'+datain[i]+'.com', complete:function(r){
response.data+= (r.responseText.length);
response.pending--; // decrement pending requests count
response.cb(); // call the callback
}});
}(i));
response.fordone = true; // mark the loop as done
response.cb(); // call the callback
}());
This isn't all very elegant but it does the job.
Is there any better way to do it? Perhaps a wrapper?
Async JS to the rescue (for both client-side and server-side JavaScript)! Your code may look like this (after including async.js):
var datain = ['google','facebook','youtube','twitter'];
var calls = [];
$.each(datain, function(i, el) {
calls.push( function(callback) {
$.ajax({
url : 'http://www.' + el +'.com',
error : function(e) {
callback(e);
},
success : function(r){
callback(null, r);
}
});
});
});
async.parallel(calls, function(err, result) {
/* This function will be called when all calls finish the job! */
/* err holds possible errors, while result is an array of all results */
});
By the way: async has some other really helpful functions.
By the way 2: note the use of $.each.
You can use the jQuery Deferred object for this purpose.
var def = $.when.apply(null, xhrs) with xhrs being an array containing the return values of your $.ajax() requests. Then you can register a callback def.done(function() { ... }); and use the arguments array-like object to access the responses of the various requests. to properly process them, remove your complete callback and add dataType: 'text' and use the following callback for done():
function() {
var response = Array.prototype.join.call(arguments, '');
// do something with response
}
function near_home(lat,long,miles) {
var url = "api_url";
var places = [];
$.getJSON(url, function(data) {
$.each(data['results'], function(i, place) {
places.push(place);
});
console.log(places);
});
console.log(places);
return places;
}
So the first console.log() will return the desired objects. But the second console method results in null data. I've rewritten this thing a few times over and can't seem to find reason for this madness. What am I missing?
AJAX requests are asynchronous. You need to execute all following code in a callback.
A possible solution would be this:
function near_home(lat,long,miles,cb) {
var url = "api_url";
var places = [];
$.getJSON(url, function(data) {
$.each(data.results, function(i, place) {
places.push(place);
});
cb(places);
});
}
When using the function, call it like this:
near_home(lat, long, miles, function(places) {
// your code using the returned array
});
The getJSON() method is asynchronous so places will not have been filled at the second console.log-statement.