Reusing a JavaScript AJAX call before another AJAX call - javascript

I have two buttons that both performs AJAX call:
$("#save").click(function() {
$.ajax({
type: "POST",
url: saveEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
})
$("#tag-as-final").click(function() {
$.ajax({
type: "POST",
url: finalizeEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
})
The requirement is that when the user click the finalize button, the system will first perform a save before actually tagging it as final. To reuse the code attached to the save button, I call the onclick listener of the save button before the actual AJAX call like this:
$("#tag-as-final").click(function() {
$("#save").click()
^^^^^^^^^^^^^^^^^^
$.ajax({
type: "POST",
url: finalizeEntryURL,
But it will not do "save-and-finalize-after" behavior since both AJAX calls are asynchronous. I need to run one after another, but cannot afford to make the AJAX call of the save button synchronous (I'm doing also a lot of other things while the tagging occurs occurs). I know this would be silly but I'm thinking something similar to...
$("#tag-as-final").click(function() {
$("#save").click().peformAsyc()
^^^^^^^^^^^^
$.ajax({
type: "POST",
url: finalizeEntryURL,
...that will force it to finish performing first the chained function before continuing, but I know that is not available. Is there any way to do this? My current work-around is placing the same save AJAX function inside the finalize AJAX function, though it doesn't allow me to code DRY (Don't Repeat Yourself):
$("#tag-as-final").click(function() {
$.ajax({
type: "POST",
url: saveEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
$.ajax({
type: "POST",
url: finalizeEntryURL,
data: { id: $("#id").val() },
success: function(r) {
...
},
error: function(r) {
...
}
})
},
error: function(r) {
...
}
})
})

It's pretty simple, you are better using jquery "promises". Like so:
var generalSettings = { }; //Settings for AJAX call.
var jqXHR = $.ajax(generalSettings); //Do AJAX call.
generalSettings.data = 'newdata'; //update generalSettings
jqXHR.done(function(data){
$.ajax(generalSettings); //New Petition with updated settings.
});
This is using ES6 promises and jQuery promises:
function doAjaxAsPromise(settings){
return new Promise(function(resolve){
var jqXHR = $.ajax(settings);
jqXHR.done(function(data){
resolve(data);
});
});
}
var settings = { };
var petition = doAjaxAsPromise(settings);
var secondpetition = petition.then(function(data){
//work with data
//new settings
var settings = { };
return doAjaxAsPromise(settings);
});
var thirdpetition = secondpetition.then(function(data){
//work with data
//new settings
var settings = { };
return doAjaxAsPromise(settings);
});
//If needed to reuse settings object outside promise scope:
//var settings = Object.create(settings);
Some other nice thing you can do for code reuse:
function save(settings) {
var prom = doAjaxAsPromise(settings);
return prom.then(function(data){
//do something with your data.
});
}
function tagAsFinal(savedPromise, settings){
return savedPromised.then(function(){
var prom = doAjaxAsPromise(settings);
return prom.then(function(data){
//work with data;
});
});
}
$('save').on('click', function(){
save(settings); //settings = $.ajax settings.
});
$('tagAsFinal').on('click', function(){
var generalSettings = { };
var settingsone = Object.create(generalSettings);
var settingstwo = Object.create(generalSettings);
var saved = save(settingsone); //$.ajax settings.
tagAsFinal(saved, settingstwo);
});
//Can still be reduced.

Related

How to combine multiple call to Ajax Data base from different JS files

I have some code on a file that makes Ajax calls. This file is being called as a function by multiple other files that creates a new instance each time.
This is the JS code that is being called:
define(["underscore", "homeop", "domReady!"],
function (_, homeop, domready) {
var timeout = 500;
return function (opUrl, opList, onCallback) {
// IRRELEVANT CODE
var getFetch = function (optionName) {
$.ajax({
url: optionsUrl,
data: { optionNames: [optionName] },
type: "POST",
dataType: "json",
async: false,
traditional: true,
success: function (data) {
_.each(data, function (optionData, optionName) {
if (homeop.globalCache[optionName] === null) {
homeop.globalCache[optionName] = optionData;
}
});
},
error: function (message) {
console.error(message.responseText);
}
});
};
self.getInfo = function (optionName) {
if (homeop.globalCache[optionName] === undefined) {
if (!_.contains(homeop.getOption(), optionName)) {
getFetch(optionName);
}
// MORE IRRELEVANT CODE GOES HERE
In other JS files, I call the get function; for example
var these = new getOptions(optionsUrl, optionsList, onLoadCallback);
var getOpt = these.get(OptionsUrl);
The problem is I am making multiple calls to the get information from the database causing multiple call to my JS file. Each new instance of the JS file will create a ajax call.
Is there a way to wait for all the calls to be done and then get data from the database? In other words how can I somehow combine all the call to my 'getOption.js'?
Thanks
Try this.. You can also implement queue in place of stack
var optionStack = [];
var isAvailable = true;
var getFetch = function (optionName) {
if(isAvailable){
isAvilable = false; // function not available now
}
else {
optionStack.push(optionName)
return;
}
$.ajax({
url: optionsUrl,
data: { optionNames: [optionName] },
type: "POST",
dataType: "json",
async: false,
traditional: true,
success: function (data) {
_.each(data, function (optionData, optionName) {
if (homeop.globalCache[optionName] === null) {
homeop.globalCache[optionName] = optionData;
}
});
},
error: function (message) {
console.error(message.responseText);
},
done: function (){
isAvailable = true;
if(optionStack.length > 0){
getFetch(optionStack.pop());
}
}
});
};

Multiple ajax calls when previous one completes

I have these ajax calls that need to get called when the previous one is success, meaning once the first ajax is OK, call the 2nd ajax, once the 2nd ajax is OK call the 3rd one, etc so on. I started with a few ajax calls so it was fine to chain them up like this below but now I have about 20 of them and it'd be a mess to chain them up like this.
$.ajax({
url: 'urlThatWorks1',
success: function (data) {
//call someMethod1 with data;
$.ajax({
url: 'urlThatWorks2',
success: function (data) {
//call method2 with data;
//another ajax call ... so on
}
}.... 19 level deep
So I need to make it bit easier to read and maintain so I'm thinking something like
var ajaxArray = [];
var function1 = $.ajax('urlThatWorks1', data I get back from the 'urlThatWorks1' call);
myArray.push(function1);
var function2 = $.ajax('urlThatWorks2', data I get back from the 'urlThatWorks2' call);
myArray.push(function2);
//etc 19 others
myArray.each(index, func){
//Something like $.when(myArray[index].call()).done(... now what?
}
Hope this makes sense, I'm looking for a way of ajax call array from which I can call an ajax call on whose success I call the next ajax in the array. Thanks.
Create a recursive function to be called in sequence as the ajax requests return data.
var urls = [ "url.1", "url.2", ... ];
var funcs = [];
function BeginAjaxCalls()
{
RecursiveAjaxCall(0, {});
}
function RecursiveAjaxCall(url_index)
{
if (url_index >= urls.length)
return;
$.ajax(
{
url: urls[url_index],
success: function(data)
{
funcs[url_index](data);
// or funcs[urls[url_index]](data);
RecursiveAjaxCall(url_index + 1);
}
});
}
funcs[0] = function(data)
// or funcs["url.1"] = function(data)
{
// Do something with data
}
funcs[1] = function(data)
// or funcs["url.2"] = function(data)
{
// Do something else with data
}
Try
$(function () {
// requests settings , `url` , `data` (if any)
var _requests = [{
"url": "/echo/json/",
"data": JSON.stringify([1])
}, {
"url": "/echo/json/",
"data": JSON.stringify([2])
}, {
"url": "/echo/json/",
"data": JSON.stringify([3])
}];
// collect responses
var responses = [];
// requests object ,
// `deferred` object , `queue` object
var requests = new $.Deferred() || $(requests);
// do stuff when all requests "done" , completed
requests.done(function (data) {
console.log(data);
alert(data.length + " requests completed");
$.each(data, function (k, v) {
$("#results").append(v + "\n")
})
});
// make request
var request = function (url, data) {
return $.post(url, {
json: data
}, "json")
};
// handle responses
var response = function (data, textStatus, jqxhr) {
// if request `textStatus` === `success` ,
// do stuff
if (textStatus === "success") {
// do stuff
// at each completed request , response
console.log(data, textStatus);
responses.push([textStatus, data, $.now()]);
// if more requests in queue , dequeue requests
if ($.queue(requests, "ajax").length) {
$.dequeue(requests, "ajax")
} else {
// if no requests in queue , resolve responses array
requests.resolve(responses)
}
};
};
// create queue of request functions
$.each(_requests, function (k, v) {
$.queue(requests, "ajax", function () {
return request(v.url, v.data)
.then(response /* , error */ )
})
})
$.dequeue(requests, "ajax")
});
jsfiddle http://jsfiddle.net/guest271314/6knraLyn/
See jQuery.queue() , jQuery.dequeue()
How about using the Deferred approach. Something like:
var arrayOfAjaxCalls = [ { url: 'https://api.github.com/', success: function() { $("#results").append("<p>1 done</p>"); } },
{ url: 'https://api.github.com/', success: function() { $("#results").append("<p>2 done</p>"); } },
{ url: 'https://api.github.com/', success: function() { $("#results").append("<p>3 done</p>"); } },
{ url: 'https://api.github.com/', success: function() { $("#results").append("<p>4 done</p>"); } },
{ url: 'https://api.github.com/', success: function() { $("#results").append("<p>5 done</p>"); } },
{ url: 'https://api.github.com/', success: function() { $("#results").append("<p>6 done</p>"); } },
{ url: 'https://api.github.com/', success: function() { $("#results").append("<p>7 done</p>"); } },
{ url: 'https://api.github.com/', success: function() { $("#results").append("<p>8 done</p>"); } },
{ url: 'https://api.github.com/', success: function() { $("#results").append("<p>9 done</p>"); } }
];
loopThrough = $.Deferred().resolve();
$.each(arrayOfAjaxCalls, function(i, ajaxParameters) {
loopThrough = loopThrough.then(function() {
return $.ajax(ajaxParameters);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<div id="results"></div>
You could use the async library, which has a bunch of functions like waterfall or series which could solve your problem.
https://github.com/caolan/async#series
https://github.com/caolan/async#waterfall

Ajax, prevent multiple request on click

I'm trying to prevent multiple requests when user click on login or register button. This is my code, but it doesn't work. Just the first time works fine, then return false..
$('#do-login').click(function(e) {
e.preventDefault();
if ( $(this).data('requestRunning') ) {
return;
}
$(this).data('requestRunning', true);
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
success: function(msg) {
//stuffs
},
complete: function() {
$(this).data('requestRunning', false);
}
});
});
Any ideas? Thanks!
The problem is here:
complete: function() {
$(this).data('requestRunning', false);
}
this no longer points to the button.
$('#do-login').click(function(e) {
var me = $(this);
e.preventDefault();
if ( me.data('requestRunning') ) {
return;
}
me.data('requestRunning', true);
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
success: function(msg) {
//stuffs
},
complete: function() {
me.data('requestRunning', false);
}
});
});
Use on() and off(), that's what they are there for :
$('#do-login').on('click', login);
function login(e) {
e.preventDefault();
var that = $(this);
that.off('click'); // remove handler
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize()
}).done(function(msg) {
// do stuff
}).always(function() {
that.on('click', login); // add handler back after ajax
});
});
In your ajax callbacks the context (this) changes from the outer function, you can set it to be the same by using the context property in $.ajax
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
context: this, //<-----
success: function(msg) {
//stuffs
},
complete: function() {
$(this).data('requestRunning', false);
}
});
You can disable the button.
$(this).prop('disabled', true);
I have also faced a similar problem.
Just adding $('#do-login').attr("disabled", true); gives me the solution.
$('#do-login').click(function(e) {
e.preventDefault();
$('#do-login').attr("disabled", true);
.........
.........
Here do-login is button id.
I've tried this and worked very fine for me, I was having trouble that $.ajax send more request until results return,
var settings = {
"url": "/php/auth/login.php",
"method": "POST",
"timeout": 0,
"async": false,
"headers": {
"Content-Type": "application/json; charset=utf-8"
},
"data": jsondata, //data pass here is in JSON format
};
$.ajax(settings).done(function (ress) {
try{
console.log(ress, "Result from Ajax here");
}
catch(error){
alert(error);
console.log(ress);
}
});
async : false worked for me.
Thanks.
Or you can do it by $(this).addClass("disabled"); to you button or link and after click is performed, you can $(this).removeClass("disabled");.
// CSS
.disabled{
cursor: not-allowed;
}
// JQUERY
$('#do-login').click(function(e) {
e.preventDefault();
$(this).addClass("disabled");
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
context: this,
success: function(msg) {
//do more here
$(this).removeClass("disabled");
},
});
});
P.S. If you use bootstrap css, you do not need the css part.
I found the approach useful. I've implemented it as a general purpose function for jQuery with ES6.
export default function (button, promise) {
const $button = $(button);
const semaphore = 'requestRunning';
if ($button.data(semaphore)) return null;
$button.data(semaphore, true);
return promise().always(() => {
$button.data(semaphore, false);
});
}
Because $.ajax() returns a promise, you simply pass in the promise and the function takes care of the rest.
Roughly speaking, here's the usage.
import preventDoubleClick from './preventdoubleclick';
...
button.click(() => {
preventDoubleClick(this, () => $.ajax()
.done(() => { console.log("success") }));
});
This function can help you with control multi Ajax requests and it's has timeout function which can return flag status to 0 after ex. 10sec (In case the server took more than 10 seconds to respond)
var Request_Controller = function(Request_Name = '', Reactivate_Timeout = 10000)
{
var a = this;
a.Start_Request = function(){
if(window.Requests == undefined){
window.Requests = {};
}
window.Requests[Request_Name] = {'Status' : 1, 'Time': + new Date()};
}
a.End_Request = function(){
if(window.Requests == undefined){
window.Requests = [];
}
window.Requests[Request_Name] = undefined;
}
a.Is_Request_Running = function(){
if(window.Requests == undefined || window.Requests[Request_Name] == undefined){
return 0;
}else{
var Time = + new Date();
// Reactivate the request flag if server take more than 10 sec to respond
if(window.Requests[Request_Name]['Time'] < (Time - Reactivate_Timeout))
{
return 0;
}else{
return 1
}
}
}
}
To use it:
var Request_Flag = new Request_Controller('Your_Request_Name');
if(!Request_Flag.Is_Request_Running()){
Request_Flag.Start_Request();
$.ajax({
type: "POST",
url: "/php/auth/login.php",
data: $("#login-form").serialize(),
success: function(msg) {
//stuffs
},
complete: function() {
Request_Flag.End_Request();
}
});
}
for prevent multiple ajax request in whole site. For example: If use ajax request in other ajax page, Using ajax in php loop, etc, Give you multiple ajax request with one result. I have solution:
Use window.onload = function() { ... }
instead of
$(document).ready(function(){ ... });
on the main index.php page. Its will be prevent all multi request. :)

Access class variable in jquery

I have a select in my html and would like to add the options via ajax when the page loads. The options values are in my DB and I get them via a call to ajax. To do so, I'm writing a class in javascript but I just can't get my data when it runs. Please take a look :
--- Main.js ---
function MyLoader() {
this._clients = null;
this._code = null;
}
Loader.prototype = {
var context = this;
loadClients: function() {
$.ajax({
url: "my/php/",
type: "POST",
data: {...},
success: function(response) {
context._clients = response;
}
});
},
getCode: function() {...}
};
Then I have the following :
$(document).ready(function() {
var loader = new Loader();
loader.loadClients();
alert(loader._clients);
//Here I want to add my options to the select
});
My alert always returns null, and I don't understand why. I need to save my data in the class in order to access them anytime I need to.
Can you point me to the right direction to make all my stuff work ? Thank you for your answers.
Loader.prototype = { // v---callback parameter
loadClients: function(callback) {
$.ajax({
url: "my/php/",
context: this, // <---set success context
type: "POST",
data: {...},
success: callback // <---pass callback
});
},
getCode: function() {...}
};
$(document).ready(function() {
var loader = new Loader();
// v---pass callback
loader.loadClients(function(response) {
this._clients = response;
alert(this._clients);
//Here I want to add my options to the select
});
});
I believe that you need to do all of your dynamic loading inside of your 'success' callback, since it is loading asynchronously.
You need to do it inside the success callback since it's async:
Loader.prototype = {
var context = this;
loadClients: function() {
$.ajax({
url: "my/php/",
type: "POST",
data: {...},
success: function(response) {
context._clients = response;
alert(loader._clients);
//Here I want to add my options to the select
}
});
},
getCode: function() {...}
};
$(document).ready(function() {
var loader = new Loader();
loader.loadClients();
});

jQuery's AJAX call to a javascript class method

I'm a newbee about jQuery's workflow and I would like to setup a javascript class that uses an internal method to make an AJAX request. When the request returns with success, the jQuery AJAX callback should invoke a method owned by the class itself. That's the code:
function IXClock()
{
this.m_intervalID = 0;
this.startClock = function ()
{
this.m_intervalID = setInterval(this.tictac, 500);
}
this.stopClock = function ()
{
clearInterval(this.m_intervalID);
}
this.setClockTime = function(p_strTime)
{
$('#clock').html(p_strTime);
}
this.tictac = function ()
{
$.ajax
({
type: 'POST',
url: '/rap/rapClock.php',
complete: function (data)
{
this.setClockTime(data);
}
});
}
}
The class represents a clock, with an internal method (tictac) that requests "what's the time" on the server side.
After the server says the time, the jQuery's AJAX method should invoke the setClockTime method of the IXClock class. The invoke method will update the #clock div item in the html page.
The problem is that the method this.setClockTime() results unknown and the javascript return the "this.setClockTime is not a function" error.
The question is: is there a way to invoka a class method from the jQuery's AJAX callback ?
I think that the problem is that the this in your callback function is different from the this referring to IXClock. Try:
var thisClass = this ;
this.tictac = function ()
{
$.ajax
({
type: 'POST',
url: '/rap/rapClock.php',
complete: function (data)
{
thisClass.setClockTime(data);
}
});
}
Test Case (added to site which already has jQuery loaded):
function uClass () {
this.testFunction = function(input) {
alert(input) ;
}
this.ajaxFunction = function() {
var myClass = this ;
$.ajax({
type: 'POST',
url: '/',
complete: function(data) {
alert(myClass.testFunction) ;
myClass.testFunction(data) ;
this.testFunction(data) ;
}
}) ;
}
}
var k = new uClass() ;
k.ajaxFunction() ;
It happens bacause your callback function leave in global context.
You can choose 2 ways
Use .bind function to bind context to callback function http://www.robertsosinski.com/2009/04/28/binding-scope-in-javascript/
jQuery's AJAX supports transfer some data to callback function. You can write smth like this:
:
this.tictac = function () { $.ajax ({ type: 'POST', context:this, url: '/rap/rapClock.php', complete: function (data) { this.setClockTime(data); } }); }
}
this does not refer to IXClock in your ajax callback. this allways points to the current scope (have a look at this document). You need to do something like this:
this.prototype.tictac = function ()
{
var self = this;
$.ajax
({
type: 'POST',
url: '/rap/rapClock.php',
complete: function (data)
{
self.setClockTime(data);
}
});
}
You can also use jQuery's .proxy()-function for this purpose:
this.prototype.tictac = function ()
{
$.ajax
({
type: 'POST',
url: '/rap/rapClock.php',
complete: $.proxy(function (data) {
this.setClockTime(data);
}, this)
});
}
The this in the result handler is not what you expect it is. (It is not the IXClock instance)
function IXClock()
{
this.m_intervalID = 0;
}
IXClock.prototype = {
startClock: function ()
{
this.m_intervalID = setInterval(this.tictac, 500);
},
stopClock: function ()
{
clearInterval(this.m_intervalID);
},
setClockTime: function(p_strTime)
{
$('#clock').html(p_strTime);
},
tictac: function ()
{
var that = this;
$.ajax({
type: 'POST',
url: '/rap/rapClock.php',
success: function (data) { // You want success here, not complete, IMO
that.setClockTime(data);
}
});
}
}
If you ask me, that ajax call is doing evil. It does not seem to send any data, nor modify any
state on the server, but is expecting/getting/using data from the php, yet is using the POST method.
Should've been
$.get('/rap/rapClock.php', function (data) {
that.setClockTime(data);
});
One simple solution is, to keep your callback function as self = this. This will support inheritance also.
class Record{
get_data(){
self = this;
$.ajax({
type : "GET",
url : "/get_url",
dataType : "json",
contentType: "application/json; charset=utf-8",
data : {},
success : function(data){
console.log(data);
self.load_table(data);
},
});
}
static load_table(data){
console.log(data);
}

Categories