I have a page that should load after the initial page load a bit of data trough AJAX that is then used in a few functions.
So far I can only get it to work with loading the AJAX requests separately (which means the same request is called like 30 times)
What I need is the possibility to have a function that can be called multiple times, but only activates the AJAX call once and the other times gives the data back without having again the same AJAX call that already gave the data back running (cause that's redundant and not needed, the data doesn't change).
Now I could do that by simply making a call and store it in a global variable and just check if something is in this variable or not...
BUT! The "but" is the problem, that these around 20 calls that need the information the AJAX delivers happen right after the DOM is loaded, right together with the AJAX call.
And so I cannot do that, because the 20 requests happen before the first AJAX call even finished showing all data.
I tried to do some stuff with JQueries "deferred", but could only manage to do it with one call and not with multiple calls at almost the same time without that it triggers the AJAX call everytime.
But I'm sure that must be possible somehow! Nicely, without some sort of loops and timeout. I really like the idea of loading pages and parts of pages partially. Input field isn't loaded right from the start, but gets delivered as soon as it is ready, etc...
Is it? I really can't wrap my head around this one...
$(function(){
loadme1();
loadme2(); /* loaded from complete different parts in the code, so not possible to start loadme2 only after loadme1 has everything finished */
});
function getData(){
return $.get("/pathtogetthedata", {}, function(data){
});
}
function loadme1(){
getData().done(function(data){
var obj = $.parseJSON(data);
/* do something with obj */
}
}
function loadme2(){
getData().done(function(data){ //please just wait till the first call to the same method finished and give me that data or wait till it's in a global variable and I take it from there. Only make a call if there is no jquery "promise" waiting
var obj = $.parseJSON(data);
/* do something with obj */
}
}
You have to keep all the "callback" and then when the data ready, to call the callback you just saved for example:
var funcs = []
function exampleOfAjaxGetData(callback) {
funcs.push(callback)
if (funcs.length == 1) {
setTimeout(function() {
alert('This is need to be called once1')
while (funcs.length > 0)
funcs.pop()('The data return from ajax')
}, 2000)
}
}
exampleOfAjaxGetData(function(x) {
alert('I got the data:' + x)
})
exampleOfAjaxGetData(function(x) {
alert('I got the data:' + x)
})
jsFiddle: http://jsfiddle.net/yn5ayw30/
In the example I show you a function that takes 2 seconds to complete.
I called the function twice. But the "setTimeout" run only once. When setTimeout complete, it will run all the function that wait for answer.
var getDataCalled = false;
var deferred = $.Deferred();
function getData(){
if(!getDataCalled) {
getDataCalled = true;
return $.get("/", {} , function(data) {
deferred.resolve(data);
});
} else {
console.log("returning deferred");
return deferred;
}
}
How about you save when you first call your "getData" function. When it has already been called you return your own "deferred" object back and resolve it when your first ajax request is finished.
I hope this short code snippet speaks for itself and is easy to understand.
Calling getData() will now first make the ajax request and after that always return a deferred object you created yourself.
getData().done(function(data) {
console.log(data);
});
getData().done(function(data) {
console.log(data);
});
getData().done(function(data) {
console.log(data);
});
You will see there will only be one ajax request.
I can think of one solution here it is :
var adata = -1; // global variable data holder
function getdata()
{
//if ajaxx call is already done and completed then return data
if(adata != -1 && adata != -2)return adata;
if(adata == -1)
{
//function getting called first time
adata = -2; // now we change value of adata to -2
// we will use this -2 to check if ajaxx call is stil running
//do ajaxx $.get call
$.get( "url_goes_here", function( data ) {
adata = data;// assingh received data to adata, so -2 is changed now
});
//now code will move to while loop part even after first call as while loop part doesn't have condition
//thus waiting for ajaxx call to be completed even if its first call
}
while(adata == -2){
//just a loop to delay output until call finishes
}
return adata;
}
Now you can use getdata() function to achieve what you want
Related
I have the function bellow called every 5 seconds to get data from the server, which is flask/python. My question is how can I adapt the getjson call to have callback when the data is successfully retrieved.
I know there's .done .fail and so on, but I was wondering if I can keep this structure and just add bellow it, but I don't know the syntax in this particular case, hope this isn't too confusing, thanks for reading, here's the code.
// get data from the server every getDataFromServerInterval milliseconds
var getDataFromServerInterval = 5000;
function getData(){
// request timesince table entries from server for user...
$.getJSON($SCRIPT_ROOT + '/_database', {
action: "getUserTable_timesince",
username: $('input[name="username"]').val()
}, function(data) { // do something with the response data
timesince_dataBuffer = data;
});
return false; // prevent get
}
// get data from the server every getDataFromServerInterval milliseconds
setInterval(getData, getDataFromServerInterval);
You could do something like this. Instead of processing the data in getData or using a callback, take advantage of the promise that $.getJSON returns. Have a separate function that is called by the timeout which calls for the data, then processes it. It neatly separates your code out into more managable functions.
var getDataFromServerInterval = 5000;
function getData() {
return $.getJSON($SCRIPT_ROOT + '/_database', {
action: "getUserTable_timesince",
username: $('input[name="username"]').val()
}
}
function wrangleData() {
getData().then(function (data) {
console.log(data);
});
}
setInterval(wrangleData, getDataFromServerInterval);
I found a partial solution, I realized that I can add a callback at the end of the function that handles the data received, which is somewhat equivalent to .done in a different getjson call structure, I'm not sure yet if the function gets called before or after the data is received.
// global timesince buffer, holds
var timesince_dataBuffer;
// get data from the server every getDataFromServerInterval milliseconds
var getDataFromServerInterval = 5000;
function getData(){
// request timesince table entries from server for user
$.getJSON($SCRIPT_ROOT + '/_database', {
action: "getUserTable_timesince",
username: $('input[name="username"]').val()
}, function(data) { // do something with the response data
timesince_dataBuffer = data;
updateEntryStruct(); // the hope is to call this when data is received
});
return false; // prevent get
}
// get data from the server every getDataFromServerInterval milliseconds
setInterval(getData, getDataFromServerInterval);
This is the solution I came up with.
var timesince_dataBuffer;
function getData(){
// gets user's entries from sql table
$.getJSON($SCRIPT_ROOT + '/_database', { // $SCRIPT_ROOT, root to the application
action: "getUserTable_timesince",
username: $('input[name="username"]').val()
}, function(data) { // if a response is sent, this function is called
timesince_dataBuffer = data;
updateEntryStruct(); // recreate the structure of each content, buttons etc
});
return false;
}
I get the data, put in a global variable, call another function which takes that data and re-creates a structure for each object received, this way I don't recreate parts of the structure which are static, most importantly the buttons.
Another function is called every 1 second, which updates the dynamic parts.
(formatted time) passed since
(event name)
Anyway, this is actually my final project in CS50, I started by communicating with the server via form submissions, refreshing the page each time the user pressed a button, then I did it by ajax, but I was sending requests to the server every 2 seconds, and having unresponsive buttons because I would keep re-creating the buttons themselves on a time interval.
And now the page feels responsive and efficient, it's been a great learning experience.
If anyone wants to check out the code, everything is here.
https://github.com/silvermirai/cs50-final-project
It's basically a bunch of random functionality that came to mind.
The application can be found here as of now.
http://ide502-silvermirai.cs50.io:8080/
How can I get some javascript to run before a document ready function?
I have the following snippet finished first...
var applicantlist = [];
$.getJSON("apps.json", function(appsdata) {
for (var i = 0; i<appsdata.applications.length; i++){
var tempapp = [appsdata.applications[i].name, appsdata.applications[i].server];
applicantlist.push(tempapp);
}
});
I've tested this, and the data gets pushed into the array just fine. The problem is that I need this array to make some ajax calls that are found in my page ready function as follows...
$(document).ready(function(){
window.jsonpCallbacks = {};
alert(applicantlist.length);
for (var i = 0; i < applicantlist.length; i++){
(function(index){
window.jsonpCallbacks["myCallback" + index] = function(data){
myCallback(data,index);
};
})(i);
//Jquery/Ajax call to the WoW API for the data.
$.ajax({
"url":"http://us.battle.net/api/wow/character/" + applicantlist[i][1] + "/" + applicantlist[i][0] + "?jsonp=jsonpCallbacks.myCallback" + i,
"type":"GET",
"data": { fields: "items, talents, progression, professions, audit, guild, stats"},
"dataType":"jsonp",
"contentType":"application/json",
"jsonpCallback":"jsonpCallbacks.myCallback"+i,
"success":function(data1){
}
})
}
All of this fires off before the first snipet, no matter where I seem to put it. So, the array is empty (the alert message just shows "0").
As you can see by the URL of my ajax call, I need that array populated beforehand. I've tried putting the first snippet in a seperate .js file and calling it before all other javascript files on the actual HTML page...
What am I missing?
Move the code that sends the first request to the document.ready. You don't usually want anything happening before the document is ready. Then move the code that sends the next request(s) to the callback of the first request after you populate the array and do whatever else you need to happen first
$(document).ready(function () {
$.getJSON("apps.json", function(appsdata) {
...
// add items to your array
sendNextRequest();
}
});
function sendNextRequest() {
//Jquery/Ajax call to the WoW API for the data.
...
}
This gurantees that the calls to the WoW api don't get fired until the first $.getJSON call completes and you populate your array.
FYI this is a common challenge in javascript. You need one operation to run only after another finishes. When you use ajax, you have callbacks like in my example above that help you achieve this. Outside of ajax requests, you can use jQuery Promises to defer tasks until after something else finishes.
Introduction to the problem
I need to call an asynchronous function within a loop until a condition is satisfied. This particular function sends a POST request to a website form.php and performs some operations with the response, which is a JSON string representing an object with an id field. So, when that id is null, the outer loop must conclude. The function does something like the following:
function asyncFunction(session) {
(new Request({
url: form.php,
content: "sess=" + session,
onComplete: function (response) {
var response = response.json;
if (response.id) {
doStaff(response.msg);
} else {
// Break loop
}
}
})).get();
}
Note: Although I've found the problem implementing an add-on for Firefox, I think that this is a general javascript question.
Implementing the loop recursively
I've tried implementing the loop by recursivity but it didn't work and I'm not sure that this is the right way.
...
if (response.id) {
doStaff(response.msg);
asyncFunction(session);
} else {
// Break loop
}
...
Using jsdeferred
I also have tried with the jsdeferred library:
Deferred.define(this);
//Instantiate a new deferred object
var deferred = new Deferred();
// Main loop: stops when we receive the exception
Deferred.loop(1000, function() {
asyncFunction(session, deferred);
return deferred;
}).
error(function() {
console.log("Loop finished!");
});
And then calling:
...
if (response.id) {
doStaff(response.msg);
d.call();
} else {
d.fail();
}
...
And I achieve serialization but it started repeating previous calls for every iteration. For example, if it was the third time that it called the asyncFunction, it would call the same function with the corresponding parameters in the iterations 1 and 2.
Your question is not exactly clear, but the basic architecture must be that the completion event handlers for the asynchronous operation must decide whether to try again or to simply return. If the results of the operation warrant another attempt, then the handler should call the parent function. If not, then by simply exiting the cycle will come to an end.
You can't code something like this in JavaScript with anything that looks like a simple "loop" structure, for the very reason that the operations are asynchronous. The results of the operation don't happen in such a way as to allow the looping mechanism to perform a test on the results; the loop may run thousands of iterations before the result is available. To put it another way, you don't "wait" for an asynchronous operation with code. You wait by doing nothing, and allowing the registered event handler to take over when the results are ready.
Thank you guys for your help. This is what I ended doing:
var sess = ...;
Deferred.define(this);
function asyncFunction (session) {
Deferred.next(function() {
var d = new Deferred();
(new Request({
url: form.php,
content: "sess=" + session,
onComplete: function (response) {
d.call(response.json);
}
})).get();
return d;
}).next(function(resp) {
if (resp.id) {
asyncFunction(session);
console.log(resp.msg);
}
});
}
asyncFunction(sess);
Why wouldn't you just use a setInterval loop? In the case of an SDK-based extension, this would look like:
https://builder.addons.mozilla.org/addon/1065247/latest/
The big benefit of promises-like patterns over using timers is that you can do things in parallel, and use much more complicated dependencies for various tasks. A simple loop like this is done just as easily / neatly using setInterval.
If I correctly understand what you want to do, Deferred is a good approach. Here's an example using jQuery which has Deferred functionality built in (jQuery.Deferred)
A timeout is used to simulate an http request. When each timeout is complete (or http request is complete) a random number is returned which is equivalent to the result of your http request.
Based on the result of the request you can decide if you need another http request or want to stop.
Try out the below snippet. Include the jQuery file and then the snippet. It keeps printing values in the console and stops after a zero is reached.
This could take while to understand but useful.
$(function() {
var MAXNUM = 9;
function newAsyncRequest() {
var def = $.Deferred(function(defObject) {
setTimeout(function() {
defObject.resolve(Math.floor(Math.random() * (MAXNUM+1)));
}, 1000);
});
def.done(function(val) {
if (val !== 0)
newAsyncRequest();
console.log(val);
});
};
newAsyncRequest();
});
Update after suggestion from #canuckistani
#canuckistani is correct in his answer. For this problem the solution is simpler. Without using Deferred the above code snippet becomes the following. Sorry I led you to a tougher solution.
$(function() {
var MAXNUM = 9;
function newAsyncRequest() {
setTimeout(function() {
var val = Math.floor(Math.random() * (MAXNUM+1));
if (val !== 0)
newAsyncRequest();
console.log(val);
}, 1000);
}
newAsyncRequest();
});
I have function LoadTempMovieList(), and need to load movies from sessionStorage. But it seems that the execution time of the for loop is faster than the AJAX call I'm making can respond, so the order of final output is not correct sometimes. How can I solve this problem?
function LoadTempMovieList(){
var obList = [];
if(sessionStorage.struct != null){
alert(sessionStorage.struct);
obList = sessionStorage.struct.split(",");
for(var i=0; i<obList.length;i++){
MovieLoader(obList[i],"movie");
//it use setTimeOut(), the problem also present
}
}
}
update
function MovieLoader(name,type,movieArray){
$.ajax({
...
data:{shortName:name,type:type},
dataType:'html',
success:function (html){
if(html!="0"){
...
}else{
...
}
}
});
}
I'm introducing recursion to load the objects in the order they're in the array. I'm also introducing some code that you may not feel you need to include to verify that we've got an array (in case some errant other function calls this, or whatever)
function LoadTempMovieList(){
var obList = [];
if(sessionStorage.struct != null){
alert(sessionStorage.struct);
obList = sessionStorage.struct.split(",");
LoadMoviesInOrder(obList);
}
}
function LoadMoviesInOrder(movies){
if( Object.prototype.toString.call( movies ) === '[object Array]' ){
//get the very first object in the array, take it off the array
var movie = movies.shift();
MovieLoader(movie,"movie",movies);
}
}
function MovieLoader(name,type,movieArray){
$.ajax({
...
data:{shortName:name,type:type},
dataType:'html',
success:function (html){
if(html!="0"){
...
if (movieArray.length) { //test to see if there are more movies left by using truthiness
//wait 50 ms and call this function again, so that we achieve recursion
setTimeout(function(){LoadMoviesInOrder(movieArray); }, 50);
}
}else{
...
}
}
});
}
If your ajax call that you refered to in your original question (before someone else edited that out) is asynchronous, then you will have to use the completion function of the ajax call to trigger the next call to MovieLoader.
Since ajax calls take an indeterminate amount of time to complete, it is not completely reliable to try to use some sort of setTimeout() to guess how long an ajax call takes. The only 100% reliable way to sequence the ajax results is to sequence the ajax calls and not launch the next ajax call until the first one has completed, etc...
You don't show us your actual ajax call so we can't be more specific on the best way to implement this.
I'm accessing a json file which has 50 entries per page over x amount of pages.
I have the total number of entries, say 500 - which amounts to 10 pages.
I get the data from json file for page 1, pass the data to an array and then repeat the function but this time for page 2.
I have created the function and it loops perfectly incrementing and fetching each page, but it doesn't wait for the json data to be parsed and passed to the array before looping again.
Basically I want to wait until the data has been processed and then continue on.
My code so far is roughly this:
function getJsonData(metroID){
currentPageNo = 0;
totalPages = 'x';
count = 0;
function jsonLoop(){
meroAreaSearchString = 'http://jsonurl'+currentPageNo;
$.getJSON(meroAreaSearchString,{},function( data ){
if(totalPages == 'x'){
var totalEntries = data.resultsPage.totalEntries;
var perPage = data.resultsPage.perPage;
totalPages = (totalEntries/perPage);
log(totalEntries+', '+perPage+', '+totalPages);
log(Math.round(totalPages));
}
$.each(data.resultsPage.results.event, function(i,item){
var name = item.displayName;
var type = item.type;
var valueToPush = new Array();
valueToPush[0] = name;
valueToPush[1] = type;
valueToPush[3] = count;
locations.push(valueToPush);
count++;
});
});
if(currentPageNo == totalPages){
log(locations);
alert('finished processing all results');
}else{
currentPageNo++;
jsonLoop();
}
currentPageNo++;
jsonLoop();
}
}
Have you tried making the request syncronous?
Just put this piece of code at the top of your function getJsonData
$.ajaxSetup({async:false});
You can specify the async option to be false to get a synchronous Ajax request. This will stop your function until the callback set some data.
The $.getJSON() function fires off an AJAX request, and calls it's callback function when the AJAX call resolves successfully, if that makes any sense.
Basically, that just means that given a call $.getJSON(url,data,callback);, jQuery will fire an AJAX request to url passing data along with it, and call callback when that call resolves. Clear cut straightforward.
The thing you're missing here is that an AJAX call is just that -- as its name implies, its asynchronous. This means that throughout the whole lifetime of the AJAX call, it lets the other logic in your application run instead of waiting for it to finish.
So something like this:
$.getJSON(url, data, callback);
alert('foo');
... will most probably result in an alert() call happening before your AJAX call completes. I hope that made sense.
To make sure that something happens after your AJAX call completes, you put that logic inside the callback. That's really what the callback is for.
$.getJSON(url, data, function (d) {
something_you_want_done_after_ajax_call();
});
In the context of your problem, you just have to put all that conditional recalling of jsonLoop() into your callback. It's not very obvious right now because of your indenting, but it's currently outside your callback.