How to sequence a recursive callback in Javascript? - javascript

I need to execute 3 ajax requests. I know that they happen to be asynchronous by default (And making them synchronous messes up the VM, so I don't want to go that way.) The way I do it is by calling a function three times passing variables.
result = '';
parse(var1);
parse(var2);
parse(var3);
view();
function parse(variable) {
//ajax request here
$.ajax({
type: 'POST',
url: 'script.php',
data: {variable: variable},
success: function (data) {
//result stored in a global variable
result += data;
}
});
}
function view() {
//do something with result
}
But right now, the view() is triggered right away when the result isn't done cooking. How do I set them up to happen one after the other? I read about callbacks but they are very confusing since I don't have 3 distinct functions but just one taking different variables.

You could store your variables in an array and use a function to make your ajax call:
var variables = [var1, var2, var3];
function callParse() {
if(variables.length) {
var currentVar = variables.shift();
parse(currentVar);
}
else {
view();
}
}
function parse(variable){
//ajax request here
$.ajax({
type:"POST",
url:'script.php',
data:{variable:variable},
success:function(data)
{
result+=data;
callParse();
}
});
}
function view(){
//do something with result
}

Try chained promises - from: https://css-tricks.com/multiple-simultaneous-ajax-requests-one-callback-jquery/
$.when(
// Get the HTML
$.get("/feature/", function(html) {
globalStore.html = html;
}),
// Get the CSS
$.get("/assets/feature.css", function(css) {
globalStore.css = css;
}),
// Get the JS
$.getScript("/assets/feature.js")
).then(function() {
// All is ready now, so...
// Add CSS to page
$("<style />").html(globalStore.css).appendTo("head");
// Add HTML to page
$("body").append(globalStore.html);
});

You could try doing it this way:
parseAndView([var1, var2, var3]);
function parseAndView(vars, index) {
index = index || 0; //initialize index if not passed
//execute the AJAX call only if there are more variables to parse
if (index < vars.length)
//ajax request here
$.ajax({
type: "POST",
url: 'script.php',
data: {variable: vars[index]},
success: function (data) {
// result stored in a global variable
result += data;
// recursive call to parse another variable
parseAndView(vars, index++);
}
});
else
view();
}
function view() {
//do something with result
}

Related

jQuery AJAX issue? Or JS OOP scope issue?

I am trying to create a database handler class in javascript. I would like to call the class by simply using:
var databaseHandler = new DatabaseHandler();
result = databaseHandler.getResult("SELECT * FROM login");
I have created the class and used a callback for the ajax function (so as to wait for the ajax result to be returned). But all I am still receiving "undefined" as my result. If I use console.log(a) inside of the onComplete function, I get an array of the intended results.
(function(window){
//Database class
function DatabaseHandler(){
//Query
this.query = function(query, whenDone){
request = $.ajax({
url: "../optiMizeDashboards/php/DatabaseQuery.php",
type: "POST",
data: {query : query},
dataType: "JSON"
});
request.done(function(output) {
whenDone(output);
});
request.fail(function(jqXHR, textStatus) {
console.log(textStatus);
});
};
//Get result
this.getResult = function(query){
this.query(query, this.onComplete);
};
//Ajax callback
this.onComplete = function(a){
return a;
};
}
//Make available to global scope
window.DatabaseHandler = DatabaseHandler;
}(window))
My question is: Is this something to do with the variable scope, or the way that ajax works? I have read all the answers explaining that ajax is ASYNC and I thought I had handled that by using a callback function "onComplete"
Any help on this topic would be greatly appreciated!
You will not be able to return result immediately from calling getResult because underlying jQuery POST request is Asynchronous, instead you need to be passing a callback function which eventually will receive a result from server.
something like that:
(function(window){
//Database class
function DatabaseHandler(){
//Query
this.query = function(query, whenDone){
request = $.ajax({
url: "../optiMizeDashboards/php/DatabaseQuery.php",
type: "POST",
data: {query : query},
dataType: "JSON"
});
request.done(function(output) {
whenDone(output);
});
request.fail(function(jqXHR, textStatus) {
console.log(textStatus);
});
};
//Get result
this.getResult = function(query, callback){
this.query(query, callback);
};
}
//Make available to global scope
window.DatabaseHandler = DatabaseHandler;
}(window))
// then use it like so
var databaseHandler = new DatabaseHandler();
result = databaseHandler.getResult("SELECT * FROM login", function(data) {
//do something with data
});
PS: exposing direct SQL access to the databse on the client is very dangerous though, and I would not recommend doing that

Passing data from variables in JavaScript

I have created a small JavaScript application with the following function that calls a function to retrieve JSON data:
var months = function getMonths(){
$.getJSON("app/data/Cars/12Months", function (some_data) {
if (some_data == null) {
return false;
}
var months_data = new Array();
var value_data = new Array();
$.each(some_data, function(index, value) {
months_data.push(index);
value_data.push(value);
});
return[months_data,value_data];
});
}
I have then created, in the same file, another function that does something when a specific page is loaded. In this function the variable 'months' is passed to the variable 'result'.
$(document).on('pageshow', '#chartCar', function(){
$(document).ready(function() {
var result = months;
var date = result[0];
var values = result[1];
//more code here...
});
}
the problem is that, based on the debugger, the getMonths() function works fine and produces the expected output, but the 'result' variable in the second function can't obtain the values passed to it by the variable 'months'. Do you know how to solve this issue?
The problem is that you $.getJSON() function is asynchronous, so your data gets loaded later then you read it. There're two workarounds:
1. Replace your $.getJSON with $.ajax and setting async: false;
2. Put your code in $.getJSON callback:
var months = function getMonths(){
$.getJSON("app/data/Cars/12Months", function (some_data) {
if (some_data == null) {
return false;
}
var months_data = new Array();
var value_data = new Array();
$.each(some_data, function(index, value) {
months_data.push(index);
value_data.push(value);
});
var date = months_data;
var values = value_data;
//more code here..
})
}
There must be a syntax error.
replace
});
}
With
});
});
$.getJSON() is a wrapper around $.ajax which is async by default. But you treat it like a sync call.
You can use $.ajaxSetup()
$.ajaxSetup( { "async": false } );
$.getJSON(...)
$.ajaxSetup( { "async": true } );
or use $.ajax with async: false
$.ajax({
type: 'GET',
url: 'app/data/Cars/12Months',
dataType: 'json',
async: false,
success: function(some_data) {
//your code goes here
}
});
or if possible change the behavior of your app so that you process your data in a callback function.

How to solve this double ajax calls issue

I am trying to do 2 ajax function calls when a user clicks a button.
I have
$('.test').on('click', function(){
code.getCode();
code.getText();
})
code.prototype.getCode=function(){
var call=//call ajax
call.callback= function(data){
//parse return data
}
}
code.prototype.getText=function(){
var call=//call ajax
call.callback= function(data){
//parse return data
}
}
I can only do 1 ajax call and only 1 ajax call will return data.
I am not sure how to solve this. Any ideas? Thanks a lot!
I am not sure if I understood correctly, but I think you are looking for a single callback from both the ajax calls..
You should use $.when.done. See below,
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively */
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
if ( /Whip It/.test(jqXHR.responseText) ) {
alert("First page has 'Whip It' somewhere.");
}
});
Not sure if you looking for sequencing it or trying to have 1 callback.
You could also have the first ajax call, call the second on success:
$('.test').on('click', function(){
var datastr = "your data";
$.ajax({
type: "POST",
url: "url",
data: datastr,
success: function(successMsg) {
//ajax done
if (/* not yet complete */) {
setTimeout(secondAjaxCall(),500);
} else {
secondAjaxCall();
}
}
});
});
You could just save each return in the code object (since you are in a different context you have to work around "this"). Then you can use a third function that checks if the data objects are loaded... and start parsing once both are there.
$('.test').on('click', function(){
code.getCode();
code.getText();
})
code.prototype.getCode=function(){
var call=//call ajax
var that = this;
call.callback= function(data){
//parse return data
that.codeData = data;
parseData();
}
}
code.prototype.getText=function(){
var call=//call ajax
var that = this;
call.callback= function(data){
//parse return data
that.textData = data;
parseData();
}
}
function parseData() {
if(!code.textData || !code.codeData) return;
... work with both
}

How to create jquery ajax as separate function?

I want to create a separate function to get specific data from Facebook graph JSON.
For example, I have the load() and called getNextFeed() function.
The getNextFeed works correctly. Except that returning value of aString is not successful.
When I pop alert(thisUrl). It said undefined.
Note: I am new to Javascript and Jquery. Please give me more information where I did wrong. Thank you.
function load()
{
$(document).ready(function() {
var token = "AccessToken";
var url = "https://graph.facebook.com/me/home?access_token=" + token;
var thisUrl = getNextFeed(url);
alert(thisUrl); // return undefined
});
function getNextFeed(aUrl)
{
$.ajax({
type: "POST",
url: aUrl,
dataType: "jsonp",
success: function(msg) {
alert(msg.paging.next); // return correctly
var aString = msg.paging.next;
alert(aString); // return correctly
return aString;
}
});
}
The problem is that $.ajax() is an ansynchronous function, means, when called, it returns in the same instance, but an ajax call is done in a separate thread. So your return vaule of $.ajax() is always undefined.
You have to use the ajax callback function to do whatever you need to do: Basically you already did it correctly, just that return aString does not return to your original caller function. So what you can do is to call a function within the callback (success()), or implement the logic directly within the success() function.
Example:
function load()
{
$(document).ready(function() {
var token = "AccessToken";
var url = "https://graph.facebook.com/me/home?access_token=" + token;
getNextFeed(url);
alert('Please wait, loading...');
});
function getNextFeed(aUrl)
{
$.ajax({
type: "POST",
url: aUrl,
dataType: "jsonp",
success: function(msg) {
alert(msg.paging.next); // return correctly
var aString = msg.paging.next;
alert(aString); // return correctly
do_something_with(aString);
}
});
}

Programming pattern to flatten deeply nested ajax callbacks?

I've inherited JavaScript code where the success callback of an Ajax handler initiates another Ajax call where the success callback may or may not initiate another Ajax call. This leads to deeply nested anonymous functions. Maybe there is a clever programming pattern that avoids the deep-nesting and is more DRY. Also, there is the problem of inner variables myVar1 and myVar2 that are used throughout the functions.
jQuery.extend(Application.Model.prototype, {
process: function() {
var myVar1;
// processing using myVar1;
jQuery.ajax({
url:myurl1,
dataType:'json',
success:function(data) {
var myVar2;
// process data using myVar1, set state of myVar2,
// then send it back
jQuery.ajax({
url:myurl2,
dataType:'json',
success:function(data) {
// do stuff with myVar1 and myVar2
if(!data.ok) {
jQuery.ajax({
url:myurl2,
dataType:'json',
success:mycallback
});
}
else {
mycallback(data);
}
}
});
}
});
}
});
There's no need for all the callbacks to be anonymous and defined inline, you can declare them elsewhere and just use the function name when specifying the callback.
Thanks to the chaining hint and this comment, I have come to the following solution. I have tested it and it works. There are probably some scope issues and you could refactor a general ChainAjax class out of it. But for the time being, this is ok.
jQuery.extend(MyApplication.Model.prototype, {
process: function() {
// private class for executing the Ajax calls
var myAjaxCalls = function(options) {
this.options = options;
this.myVar1 = null;
this.myVar2 =null;
}
jQuery.extend(myAjaxCalls.prototype, {
process1:function(data) {
// processsing using this.myVar1
this.myVar1 = 5;
return true;
},
process2:function(data) {
this.myVar2 = 6;
if(data.ok) {
mycallback(data);
}
else {
return true;
}
},
process3:function(data) {
// Process this.myVar1 and this.myVar
mycallback(data);
return false;
},
chainAjax:function() {
if(this.options.length > 0) {
var opt = this.options.shift();
var that = this;
jQuery.ajax({
url:opt.url,
success:function(data) {
if(that[opt.callback](data)) {
that.chainAjax();
}
}
});
}
}
});
// End private class
var calls = new myAjaxCalls([
{url:'http://localhost/', callback:'process1'},
{url:'http://localhost/', callback:'process2'},
{url:'http://localhost/', callback:'process3'}
]);
calls.chainAjax();
}
});
Update: I found this nice presentation that also deals with useful programming patterns and best practices.
Update 2012: In the meantime there are several libraries for simulating a synchronous flow with asynchronous functions: q, stratified.js and streamline.js
I would suggest creating a little tool called "chain ajax". You give it what you want to happen in what order, and then fire. It will chain ajax on success until all the logic runs out. It will help you stop repeating yourself and just represent the logical model of what you want done vs grunt-coding.
You could do this with Frame.js like this:
jQuery.extend(Application.Model.prototype, {
process: function() {
var myVar1;
// processing using myVar1;
Frame(function(done){
jQuery.ajax({
url:myurl1,
dataType:'json',
success: done
});
});
Frame(function(done, data) {
var myVar2;
// process data using myVar1, set state of myVar2,
// then send it back
jQuery.ajax({
url:myurl2,
dataType:'json',
success: done
});
});
Frame(function(done, data) {
// do stuff with myVar1 and myVar2
if(!data.ok) {
jQuery.ajax({
url:myurl2,
dataType:'json',
success:done
});
}
else {
done(data);
}
});
Frame(function(done, data){
mycallback(data);
});
Frame.start();
}
});

Categories