jQuery Ajax promise queue not working when success function fails - javascript

I have a single-page app that uses a queuing mechanism based on promise, something like this:
a) a function that handles ajax requests
function AjaxSender(SomeAjaxData, FunctionToCallBack, SomeCallBackData) {
return $.ajax({
url: ...,
type: "POST",
data: SomeAjaxData,
success: function (msg, textStatus, request) {
if (FunctionToCallBack) {
FunctionToCallBack(SomeCallBackData);
//problem if there's a bug when this executes
}
}
});
}
b) a function that uses a promise object to queue requests
var AppAjaxPromise;
function AjaxRequestQueue(SomeAjaxData, FunctionToCallBack, SomeCallBackData) {
if (AppAjaxPromise) {
AppAjaxPromise = AppAjaxPromise.then(function () {
return AjaxSender(SomeAjaxData, FunctionToCallBack, SomeCallBackData);
});
return AppAjaxPromise;
}
AppAjaxPromise = AjaxSender(SomeAjaxData, FunctionToCallBack, SomeCallBackData);
return AppAjaxPromise;
}
When I want to send an ajax request, I call AjaxRequestQueue(TheAjaxData, TheFunctionToCallBack, TheCallBackData) and the queuing mechanism ensures that if multiple requests are sent simultaneously, or before one has finished returning, they are queued and processed one after the previous one is done.
The problem occurs when a bug stops the execution of the callback function. If that function bugs, the whole queuing mechanism stops and calling AjaxRequestQueue doesn't trigger ajax requests any more.
What do I need to do to fix this?

As jQuery's $.ajax returns a promise (and since you are using it), abandon the use of the success callback. Instead move that code in a then callback. This will allow you to chain a catch method (jQuery 3.x) call to it to respond to errors. If you don't trigger another error in that catch callback, the promise it returns will be resolved again (not rejected), so the rest of your chain will not be aborted:
function ajaxSender(someAjaxData, functionToCallBack, someCallBackData) {
return $.ajax({
url: ...,
type: "POST",
data: someAjaxData
}).then(function (msg, textStatus, request) {
if (functionToCallBack) {
functionToCallBack(someCallBackData);
}
}).catch(function (err) {
console.log('error occurred, but request queue will not be interrupted', err);
});
}
jQuery 2.x
The above needs jQuery 3.x. In jQuery versions before 3.x, you can replace the catch method like this (notice the null argument):
...
}).then(null, function (err) {
...
...but jQuery 2.x promises are not Promise/A+ compliant, which makes it a pain to get it right. Here is how you could do it for jQuery 2.x. This snippet uses a URL that mimics a delay and an HTTP response status code, which allows it to test request errors, JavaScript run time errors, and sequencing:
function ajaxSender(someAjaxData, functionToCallBack, someCallBackData) {
return $.ajax({
// URL for demo: server will use the sleep parameter in the data,
// and will return the given HTTP status
url: "http://httpstat.us/" + someAjaxData.status,
type: "GET", // The demo URL needs a GET
data: someAjaxData
}).then(function (data) {
if (functionToCallBack) {
try { // Would not be necessary if jQuery 2.x were Promise/A+ compliant
functionToCallBack(someCallBackData);
} catch (e) {
console.log(someCallBackData, 'Error occurred during callback');
}
}
}, function (err) { // This second function captures ajax errors
console.log(someCallBackData, 'HTTP error');
// Return a resolved promise.
// This would not be necessary if jQuery 2.x were Promise/A+ compliant
return $.when();
}); // In jQuery 3.x you would chain a catch call here instead of the try/catch.
}
var appAjaxPromise = $.when();
function ajaxRequestQueue(someAjaxData, functionToCallBack, someCallBackData) {
appAjaxPromise = appAjaxPromise.then(function () {
return ajaxSender(someAjaxData, functionToCallBack, someCallBackData);
});
return appAjaxPromise;
}
// Demo: the ajax data argument is also used to define the HTTP response status and
// the sleep time, and the data argument identifies the number of the call
// Survive an HTTP error
ajaxRequestQueue({ status: 404, sleep: 1000 }, myCallBack, 1);
// Survive a runtime error in the callback
ajaxRequestQueue({ status: 200, sleep: 2000 }, myErrorGeneratingCallBack, 2);
// Demo that the callback calls remain in the right order
ajaxRequestQueue({ status: 200, sleep: 3000 }, myCallBack, 3);
ajaxRequestQueue({ status: 200, sleep: 2000 }, myCallBack, 4);
ajaxRequestQueue({ status: 200, sleep: 1000 }, myCallBack, 5);
function myCallBack(data) {
console.log(data, "My callback is called");
}
function myErrorGeneratingCallBack(data) {
console.log(data, "My callback is called");
throw "I threw an error in my callback";
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
You can still continue the above pattern when moving to jQuery 3: it will still work. But ideally, you should then migrate the code to the catch-based version that I provided at the top.
Some other remarks
There is a consensus to only capitalise the first letter of a variable when it is a constructor/class.
By initialising appAjaxPromise as an immediately resolved promise, you can avoid code repetition:
var appAjaxPromise = $.when();
function ajaxRequestQueue(someAjaxData, functionToCallBack, someCallBackData) {
appAjaxPromise = appAjaxPromise.then(function () {
return ajaxSender(someAjaxData, functionToCallBack, someCallBackData);
});
return appAjaxPromise;
}

I am not sure if this answer will fix it, but the callbacks you are using in the success function might not be accessible from there.
You can add extra data to the request like so... and it will be accessible with 'this....'. (see success).
Not sure if you should ether :p I have been doing this to pass data from inside an object without having to change the ajax' context or using $.proxy. Also I have been able to access an object's function that fires a request, from inside that request's success, making it recursive for sending files in chunks.
If there are any comments on doing this, I would love to hear.
return $.ajax({
FunctionToCallBack: FunctionToCallBack,
SomeCallBackData: SomeCallBackData,
url: ...,
type: "POST",
data: SomeAjaxData,
success: function (msg, textStatus, request) {
if (this.FunctionToCallBack) {
this.FunctionToCallBack(this.SomeCallBackData);
//problem if there's a bug when this executes
}
}
});

Related

Javascript Ajax Uncaught (in promise) undefined

I'm struggling with an ajax GET using a promise.
If the ajax runs into an error, such as the URL is incorrect, the 'error: function ()' is called and I get an error in the browser debug saying "Uncaught (in promise) undefined"
The first two parts of the ajax function works OK i.e. if the ajax returns a success, then both inner conditions for result.success true/false are working, I just don't know how to resolve the reject call in the ajax error function at the bottom:
var myVariable = "test";
let promise = myFunction(myVariable);
promise.then(function () {
// Do Something
}).catch(function (message) {
console.error(message);
});
function myFunction(myVariable) {
return new Promise(function (resolve, reject) {
$.ajax({
type: "GET", //send it through get method
url: "/myURL/Edit?handler=GetBlahBlah",
data: {
sourceType: myVariable
},
contentType: "json",
dataType: "json",
success: function (result) {
if (result.success == false) {
reject(result.responseText); // This works
} else {
resolve(); // This works
}
},
error: function () {
reject("Error while making Ajax call! Get Trigger"); // NOT WORKING!!
}
});
});
}
Turns out my original question does actually work correctly, I hadn't noticed at first that the calling method to this ajax GET request was coming from two different process in my javascript at different times and i hadn't included a catch block on the process that was actually calling this ajax the first time round.
Apologies if I caused any confusion to anyone, my bad!

Javascript - How to pass data from two API ajax calls to one function

I am attempting to make two seperate ajax calls to a third party API end point and display both its data on a HighChart graph. I am attempting to pass the data from both calls into my drawHighcharts function in order to display the data.
I am having issues with properly passing the data from both ajax calls. I've attempted to call back my drawHighcharts function in both my ajax calls. When console.logging both data I am only seeing the first stData but the odData is returning undefined. I am returning back stData twice and I believe that is because I am calling my function twice.
What is the proper way of handling both data into one function?
My expected outcome is to be able to access odData and stData in my drawHighCharts function.
function getStData() {
$.ajax({
type: "GET",
url: stUrl,
async: true,
success: function (stData) {
drawHighCharts(stData)
},
error: function (e) {
// handle exception
console.log("Error occured while reading resource file: ", e);
}
});
}
function getOdData() {
$.ajax({
type: "GET",
url: odUrl,
async: true,
success: function (odData) {
drawHighCharts(odData)
},
error: function (e) {
// handle exception
console.log("Error occured while reading resource file: ", e);
}
});
}
function drawHighCharts(stData, odData) {
console.log(stData, "st")
console.log(odData, "od")
}
You're getting undefined because in your ajax calls you're only passing one argument. In getStData you're passing only stData to drawHighCharts and for getOdData you're only passing odData. It would make sense why in the drawHighCharts function one of the two arguments (the second one) is undefined since you're only passing one argument. Instead, you can wrap multiple get requests in Promise.all. This way your requests will be made and when they are both finished the promise will resolve and you can access both API results in the then following the Promise.all. See below.
function getStData() {
$.ajax({
type: "GET",
url: stUrl,
async: true,
success: function (stData) {
return stData
},
error: function (e) {
// handle exception
console.log("Error occured while reading resource file: ", e);
}
});
}
function getOdData() {
$.ajax({
type: "GET",
url: odUrl,
async: true,
success: function (odData) {
return odData
},
error: function (e) {
// handle exception
console.log("Error occured while reading resource file: ", e);
}
});
}
function drawHighCharts(stData, odData) {
console.log(stData, "st")
console.log(odData, "od")
}
Promise.all([
getStData(),
getOdData(),
]).then(([stData, odData]) => drawHighCharts(stData, odData))
This post is related to fetch requests in React but Promise.all can be used with ajax in regular JS.
You can call getOdData() inside success of getStData() and pass stData to it. And then inside success of getOdData() you will have access to both stData and odData. Now you can call drawHighCharts() with both stData and odData.

How to handle ajax response in FLUX

I'm new in FLUX and I have problem how to handle ajax in FLUX.
My situation is following :
I have file commentAPI.js
//all js files are compiled from coffescript
// fetching all comments from server
_fetchComments: function() {
var promise;
promise = $.ajax({
url: "comments/show",
type: "GET",
dataType: "json"
});
return promise.then(function(response) {
// here should be any action ?
}, function(error) {
return console.log(error);
}); }
Then I have commentActions.js
fetchComments: function () {
allcomments=commentAPI._fetchComments();
return Dispatcher.dispatch({
actionType: ActionTypes.ALL_COMMENTS,
comments: allcomments
});
}
This code actually doesnt work because function _fetchComments called in commentActions.js return whole promise.
What I want to do: I would like to get response from ajax callback function and pass the result to my payload object and then dispatch it by Dispatcher in my _fetchComments() function in commentActions.js
How is the best way to do it? How can I get the access to the ajax callback function response ?
You should dispatch _fetchComments function and when that promise is resolved, invoke the action fetchComments.
In the react code you should invoke the async function (i.e. _fetchComments).
In your example:
// fetching all comments from server
_fetchComments: function() {
var promise;
promise = $.ajax({
url: "comments/show",
type: "GET",
dataType: "json"
});
return promise.then(function(response) {
// put the sync action here
fetchComments(response)
}, function(error) {
return console.log(error);
}); }
And don't forget to remove it from the action (i.e fetchComments)

Turn several ajax requests into Observables with RxJS

I'm struggling with something - which I'm guessing means I've misunderstood and am doing something silly
I have an observable and need to use it to create some object, send that to the server for processing, combine a result from the server with the object I sent, and then turn that into an observable so what I want to do (I think) is something like
var theNewObservable = my.observable.things.select(function(thing) {
var dataToSend = generateMyJavascriptObjectFrom(thing);
var promise = $.ajax({
type: 'POST',
url: http://somewhere.com,
data: dataToSend
}).promise();
return rx.Observable.fromPromise(promise).subscribe(function(data, status, jqXHR) {
var infoFromServer = jqXHR.getResponseHeader('custom-header-returned');
// I'm wanting this to be the thing other code can subscribe to
return { infoFromServer: dataToSend };
}, function(err) {
alert('PC LOAD LETTER!');
console.error(err);
});
}
});
theNewObservable.subscribe(function(combinedInfo) { console.log(combinedInfo) };
where I'm expecting {infoFromServer: dataToSend} I'm getting an AutoDetachObserver and I can see that has an onNext with the ajax onSuccess signature so I'm obviously doing something silly
A couple things that should help a bit:
1) The subscribe method is a terminal method, as in, it won't return anything. It is where the Observer attaches so there should be no further data propagation after the subscribe
2) The onNext method of subscribe can only take a single value which you will need to have all the message data wrapped in.
Since jQuery's Promise will not behave well with this, you have two options. First, you can use the RX-DOM project for an Observable ajax version. Or you will need to wrap the promise method. If you further need to wait on the response you should be using selectMany instead, which will allow you to fire off the promise, then await its return and map the response to the original request.
var theNewObservable = my.observable.things
//Preprocess this so that `selectMany` will use
//dataToSend as the request object
.map(function(thing) { return generateMyJavascriptObjectFrom(thing); })
.selectMany(function(dataToSend) {
var promise = $.ajax({
type: 'POST',
url: http://somewhere.com,
data: dataToSend
}).promise();
//Rewrap this into a promise that RxJS can handle
return promise.then(function(data, status, jqXHR) {
return {data : data, status : status, jqXHR : jqXHR};
});
}, function(request, response) {
return {
infoFromServer : response.jqXHR.getResponse('custom-header'),
dataToSend : request
};
});
theNewObservable.subscribe(
function(combinedInfo) {
console.log(combinedInfo)
},
function(err) {
alert('PC LOAD LETTER!');
console.error(err);
});

Promise deferred ajax api jQuery

I don't understand promises/deferred very much... I have something like that :
function callAjax() {
return $.ajax({
type: 'post',
dataType: 'jsonp',
data: {status: status, name: name},
url: '/test',
xhrFields: {
withCredentials: true
}
});
}
function connectAjax() {
var msg = 'doesnt work';
var promise = callAjax();
promise.then(function(data, textStatus, xhr) {
msg = 'it worked !';
}, function(data, textStatus, xhr) {
msg = 'it failed !';
});
console.log(msg); // output 'doesnt work'
}
I have tried a lot of different things (always, done, etc..) but couldn't make it work.
I use jsonp but my request isn't cross domain. I expect a 500 error from the server for my request.
For your example to work, you have to put the 'console.log(...)' statment INSIDE the two callback functions you register on the promise with .then(.., ..).
You have to keep in mind that the promise callback functions get called, only when the ajax call is finished.
Your script however does not wait until this happens and 'console.log(msg);' is executed before the ajax call returns.
This is a great example for the non-blocking nature of JavaScript.
For more detailed understanding look up resources on the JS event loop:
https://thomashunter.name/blog/the-javascript-event-loop-presentation/
Understanding the Event Loop

Categories

Resources