SOLVED: I solved my problem by doing each XMLHttpRequiest() recursively. Basically at the end of my xhr.onload, I would make another request and actively check if I've reach the end of my data - when I have I return.
I'm fairly new in JavaScript and have some familiarity with the D3 Library. I'm trying to read a CSV file from my computer using the D3 Library and sending specific information from my file to an API through an XMLHttpRequest().
With each call to the API which returns a JSON object to me, I store that object in a dataset for later use. I'm trying to have it so that my whole CSV file is read and processed before I work with the dataset, however I'm running into a problem since the API calls are asynchronous.
My code looks something like this:
var myData = [];
d3.csv("myFile.csv", function(data)
{
for (var i = 0; i < data.length; i++)
// Get appropriate data from data object
// Make API call with XMLHttpRequest() and store in myData array
});
// Handle fully updated myData array here
As it is, my code currently goes through my loop in almost an instant and makes all the API calls asynchronously and then proceeds to work on my data without waiting for anything to update.
Is there a way to ensure that my CSV file has been processed and all the API calls have returned before I can work with this dataset? I've tried callback functions and promises but had no success.
You can easily do this with a simple counter
var counter = 0;
var myData = [];
d3.csv("myFile.csv", function(data)
{
for (var i = 0; i < data.length; i++){
// Get appropriate data from data object
$.get("your/api/path", function(result){
counter++; // this is important. It increments on each xhr call made.
myData.push(result);
if(counter === data.length) cb(myData); // This will check if the current xhr request is the last xhr request. If yes then call our cb function.
});
}
});
function cb(data){
// This will run only when all the http requests are complete
// Do something with data
}
All this code does is, it makes sure that all of our requests should be completed first before calling our cb function (here you will write your further logic). This approach guarantees that cb will run only when all xhr requests are completed.
I think the answer in this post could help
d3: make the d3.csv function syncronous
You can as well use the Promise API.
Related
I am writing a JS where I want to make some Ajax calls to get a JSON file from my DB in CouchDB. My code is based on examples I found online, but my lack of experience and knowledge is making it difficult to fix it completely.
My code:
function myFunction(){
var request = $.ajax({
url:'http://admin:pass#localhost:5984/db/_design/view/_view/view',
type:'get',
dataType:'json'
});
request.done (function (data)){
var result;
for (var i in data){
if( data[i] == key){
result.push(data[i]);
}
}
console.log(result);
};}
Problem: It seems like it is not even doing the requested call since when I try to print my array it isn`t doing anything.
The way see it, in the first part where I defined request it should get the JSON file from CouchDB. And, if correct, in the second part where the request is done request.done I give the function how I want the JSON file to be taken care of. To make it clear, my idea is to iterate through the data and to save the values of the "key" in every row in my result-array.
I need a way to send multiple AJAX calls at the same time in Javascript/Angular.
After some searching i couldn't find an answer.
What i want to do is send all my requests as fast as possible.
If i execute my calls in a for loop or in a queue of promises with the $q library in Angular, a request gets sent, waits for it to execute the callback, and then sends the next one.
This is a example code:
var array = [];
Page.get({id:1}, function(result){
for(var i = 0; i < result.region[0].hotspots.length; i++){
var promise = Hotspot.get({id: result.region[0].hotspots[i].id});
array.push(promise);
}
$q.all(array).then(function(data){
console.log(data);
});
});
Page is a angular resource with a get method which requires a ID.
What i want is that they all get sent at the same time and they call their callback when ready. The order in which the calls get returned doesn't really matter.
Thanks
Think outside the box with Web Workers
An interesting aproach to solve this question, is to use web workers to execute the requests in a different thread. if you are not familiar with web workers i advice you to start by this great tutorial of techsith. Basically, you will be able to execute multiple jobs at the same time. See also the W3Schools Documentation.
This article from Html5Rocks teach us how to use Web Workers without a separate script file.
Have you tried using Async.js module?
You can achieve desired behavior using something like
Page.get({id:1}, function(result){
async.each(result.region[0].hotspots, callAsync, function(err, res){
console.log(res);
}
});
function callAsync(hotspot, callback){
callback(null, Hotspot.get({id: hotspot.id});
}
From Doc :
each(coll, iteratee, [callback])
Applies the function iteratee to each item in coll, in parallel. The
iteratee is called with an item from the list, and a callback for when
it has finished. If the iteratee passes an error to its callback, the
main callback (for the each function) is immediately called with the
error.
The $http service sends XHRs in parallel. The code below demostrates 10 XHRs being sent to httpbin.org and subsequently being received in a different order.
angular.module('myApp').controller('myVm', function ($scope, $http) {
var vm = $scope;
vm.sentList = [];
vm.rcvList = [];
//XHR with delay from 9 to 1 seconds
for (var n=9; n>0; n--) {
var url = "https://httpbin.org/delay/" + n;
vm.sentList.push(url);
$http.get(url).then(function (response) {
vm.rcvList.push(response.data.url);
});
};
//XHR with 3 second delay
var url = "https://httpbin.org/delay/3";
vm.sentList.push(url);
$http.get(url).then(function (response) {
vm.rcvList.push(response.data.url);
})
})
The DEMO on JSFiddle.
I am trying to send a xmlHttpRequest in a while loop and want to do something with the response in the same while loop. Since the requests are asynchronous, how can I achieve it? I need to execute everything serially
while(i < n){
response = self.sendHttpRequest(params);
//do something with the response
}
Any help would be appreciated.
Even If I use a callback, how can I get back to same while loop after executing the callback?
There are two ways I can think of:
1. Add a polling loop after the get call that waits until the response.readyState is set and then process the response:
while(i < n){
response = self.sendHttpRequest(params);
while( response.readyState != 4 ){
// polling wait
}
//do something with the response
}
This option is not really recommended since it stops the flow of the code and you can get stop in the loop if the readyState never changes (not likely, but possible with errors).
2. You can encapsulate the request in a function that will be called recursively when the last response handling finishes:
var i = 0;
function handle( response ){
//handle response
i++;
if( i < n ) sendRequest();
}
function sendRequest(){
// Your request setup code
response.onreadystatechange = handle;
response = self.sendHttpRequest(params);
}
The second method is preferred in my opinion, as it maintains the asynchronicity of the html request call, and doesn't stop the flow of the code, however it does "break" the loop structure. The first method keeps the loop structure, but is not very good coding practice.
Are you using any ajax library or plain js. If you are not using any library ,you can pass third argument to open method false.like below
var xmlHttp=new xmlHttpRequest();
xmlHttp.open({YOUR_METHOD},{YOUR_PATH},false);
Passing false to open method makes synchronous call .so you can handle the return in same loop.
I may be doing something very silly here. Basically a user can have multiple "sites" and for each site we have to run some calculations to come up with a total. We have a php page which does these calculations which we can call calculate.php. It returns a string ex: 50 which we then parse into a float in JS. So here's what I'm trying to do: get the total of all of the numbers outputted by calculate.php.
My idea was to loop through the sites, $.post() to calculate.php within every iteration (other things are being done too in the loop, less important) and add to a variable in the callback function. I believe my problem is that $.post() is async... Here is my sample code:
function systems(siteList){
var runningTotal = 0;
for (var ii=0,i<siteList.length,i++){
$.post("calculate.php",{foo:siteList[ii]},function(data){
// important part
runningTotal = runningTotal + data
})
}
//outside loop
alert(runningTotal)
}
This function may be imperfect, but my real question is how can I achieve the result I'm looking for here? Just so you know, runningTotal always alerts as 0 with the above code.
Thanks
EDIT: Thanks to all for your help. from what I can see, it is not wise of me to be using so many AJAX calls so instead of directly fixing this problem, I believe I will take a step back and take the advice of sending only one ajax callback to perform the task of summing for me. Thanks again
I'd suggest to make use of $.when and run a callback once all Ajax calls are done. At that moment you have access to the response of each Ajax call and you can perform any necessary computation.
For example:
function systems(siteList){
var promises = [];
for (var ii=0; i<siteList.length; i++){
// `$.post` returns a promise. We keep track of each promise.
promises.push($.post("calculate.php",{foo:siteList[ii]}));
}
$.when.apply($, promises).then(function() {
// this is called once the responses of all Ajax calls
// have been received
var sum = 0;
for (var i = 0, l = arguments.length; i < l; i++) {
sum += +arguments[i][0]; // value must be cast to number first
}
alert(sum)
});
}
Have a look at the jQuery documentation to learn more about promises: http://learn.jquery.com/code-organization/deferreds/.
In general though, it's better to make as few Ajax requests as possible. If you can change the server side code, I would change it to accept a list of values, not only one value, and let the server return the final result.
I recommend you package up your data on the client side (in JavaScript) and send it to the server in one AJAX request. Multiple requests waste bandwidth and time.
Or, if the calculations are simple and don't need a database or some other external dependency, just move your calculations to a library of javascript functions and do it all client side.
Leaving aside that there should probably be one request doing this sort of summing up the calculations on the server side (IMHO), use a counter and when it reaches 0 you know, you have all results collected:
function systems(siteList){
var runningTotal = 0;
var counter = 0;
for (var ii=0,i<siteList.length,i++){
counter++;
$.post("calculate.php",{foo:siteList[ii]},function(data){
// important part
runningTotal = runningTotal + data
counter--;
if(counter==0)
alert(runningTotal)
});
}
}
Not very fancy, but you could use a file on the server to store your running total.
As each ajax request is received, read/update total/write to a file on the server.
Then send that value back either after receiving a specific request (&request=send_total), or somehow appended to the output for each request ( 50|3753 ).
AJAX is asynchronous.
Do the alert inside of the AJAX callback.
I'm new in the scripting and web world and have been trying to work through an issue I've been having. I am reading data from a local JSON file, and have been able to use jQuery.getJSON and jQuery.parseJSON successfully, but I am trying to use the data outside of the getJSON callback function and am having issues. I think it comes down to me not fully understanding the correct way to do this, and that's where I'm looking for your help. Here's my code:
var names = new Array();
$.getJSON('ferries.json', function(data) {
var jsondata = $.parseJSON(JSON.stringify(data));
var length = jsondata.nodes.length;
for (var i = 0; i < length; i++) {
names[i] = String(jsondata.nodes[i].name);
}
});
console.log('Names: ' + names[0]);
The final line returns undefined. If I were to write that line right after the for loop, it would return the desired value. Here's how the JSON file is structured:
{
"nodes":[
{
"name":"John"
},
...
{
"name":"Joe"
}
]
}
Any help would be appreciated - thanks!
Edit: One last thing, it seems that the final line (console.log(...)) executes before the $.getJSON bit, which confuses me as well.
$.getJSON runs asynchronously. The function that you pass to it is a "callback", which means that it gets called when getJSON comes back from doing its thing.
If you want to do something with the JSON data that you get back, you must wait for the callback to execute.
Also, on a side note, $.parseJSON(JSON.stringify(data)) is redundant. The data object is already a perfectly usable object with your data in it, but you're turning that object back into a JSON string and then immediately back into an object. Just use data as is. For more information, check out the jQuery API docs for getJSON.