I'm using nested AJAX. I disable button before all AJAX calls then enable button within .always in outermost AJAX. But the problem is that I think code to enable button happens before code within .done of innermost AJAX. Is it because the .always in outermost AJAX runs in parallel to .done in innermost AJAX? And one gets done before the other?
What's the workaround to this? Do I need to use promises so that the button enabling happens after all AJAX calls have been completed? If so, can you show me how? It seems advanced and I don't understand the code I've been reading around.
function loginAndEnter() {
$("#login-and-enter-btn").prop('disabled', true);
$("#login-and-enter-btn").text('請稍等...');
$.ajax({ //Outermost AJAX
type:"GET",
url:"/qrcode/login/",
data:{
"cellphone":document.getElementById("cellphone").value,
"password":document.getElementById("password").value
}
})
.done(function(responsedata) {
var parsedJson = $.parseJSON(responsedata);
if(parsedJson.result==1){
document.getElementById("token").value = parsedJson.token;
$.ajax({
type:"GET",
url:"/qrcode/entry/",
data:{
"token":document.getElementById("token").value,
"parking_lot_id":{{ $parking_lot_id }},
"in_or_out":{{ $in_or_out }}
}
})
.done(function(responsedata) {
var parsedJson = $.parseJSON(responsedata);
if(parsedJson.result==1){
$.ajax({
type:"GET",
url:"/qrcode/zero/",
data:{
"booking_id":parsedJson.Booking_ID[0].id,
"token":document.getElementById("token").value
}
})
.done(function(responsedata) { //Innermost done
var parsedJson = $.parseJSON(responsedata);
if(parsedJson.result==1){
alert("進場成功! 請使用易停網APP繳費與出場.");
window.location.href = "/download";
}
else{
alert(parsedJson.title+"\n"+parsedJson.description);
}
})
.fail(function(xhr, status, errorThrown) {
...
});
}
else{
alert(parsedJson.title+"\n"+parsedJson.description);
}
})
.fail(function(xhr, status, errorThrown) {
...
});
}
else{
alert(parsedJson.title+"\n"+parsedJson.description);
}
})
.fail(function(xhr, status, errorThrown) {
...
})
.always(function() { //Outermost always
$("#login-and-enter-btn").prop('disabled', false);
$("#login-and-enter-btn").text('登入和升起柵欄進場');
});
}
The .always function doesn't wait for the other AJAX requests to be completed because it's invoked right after the outermost request gets a response. The fact that requests are nested, means that subsequent AJAX requests will be invoked after other ones are resolved, but if you'd like to do something only when and after all of them are resolved, Promises will be required.
I modified your code to show one way of achieving your goal with Promises and async/await function.
function firstAJAX() {
return new Promise((resolve, reject) => {
$.ajax({ //Outermost AJAX
type:"GET",
url:"/qrcode/login/",
data:{
"cellphone": 111111111111,
"password": "pwd"
}
})
.done(function(responsedata) {
// var parsedJson = $.parseJSON(responsedata);
var parsedJson = {};
parsedJson.result = 1;
if(parsedJson.result==1){
resolve(responsedata);
}
else{
alert(parsedJson.title+"\n"+parsedJson.description);
}
})
.fail(function(xhr, status, errorThrown) {
console.log(status);
});
});
}
function secondAJAX(data) {
return new Promise((resolve, reject) => {
$.ajax({
type:"GET",
url:"/qrcode/entry/",
data:{
"token": "token",
"parking_lot_id": 11,
"in_or_out": 22
}
})
.done(function(responsedata) {
// var parsedJson = $.parseJSON(responsedata);
var parsedJson = {};
parsedJson.result = 1;
if(parsedJson.result==1){
resolve(responsedata);
}
else{
alert(parsedJson.title+"\n"+parsedJson.description);
}
})
.fail(function(xhr, status, errorThrown) {
console.log(status);
});
});
}
function thirdAJAX(data) {
return new Promise((resolve, reject) => {
$.ajax({
type:"GET",
url:"/qrcode/zero/",
data:{
"booking_id": 222,
"token":"token"
}
})
.done(function(responsedata) { //Innermost done
// var parsedJson = $.parseJSON(responsedata);
var parsedJson = {};
parsedJson.result = 1;
if(parsedJson.result==1){
alert("進場成功! 請使用易停網APP繳費與出場.");
// window.location.href = "/download";
resolve(responsedata);
}
else{
alert(parsedJson.title+"\n"+parsedJson.description);
}
})
.fail(function(xhr, status, errorThrown) {
console.log(status);
});
});
}
async function loginAndEnter() {
const first = await firstAJAX();
const second = await secondAJAX(first);
const third = await thirdAJAX(second);
$("#login-and-enter-btn").prop('disabled', false);
$("#login-and-enter-btn").text('登入和升起柵欄進場');
}
So the way it works is that loginAndEnter function will wait for firstAJAX, secondAJAX and thirdAJAX to be resolved. All of these functions return Promises, which are resolved when the GET request successfully receives a response. secondAJAX and thirdAJAX accept one parameter, which is the response passed asynchronously (thanks to 'await') from the function called before it.
I changed many values for my own testing purposes so please change them back to yours before trying it out.
Related
first i call getLiveData which gets data from the server
then some parts of ui are rendered and these parts calls bindLiveData with Id
sometimes data comes later then some parts of UI thats why i wait till request is done.
The problem gots tricky when there is an exception, since the method is called itself again (in realilty up to couple of times)
I will not have calls from UI again , since they call bindLiveData once after render
so In fail method i could grab all Ids and on next successfull ajax reqest i could assinged data.
But what happens then with my infoDataPromise ?? since it will be overidden on error
Does all 'fails' of the previous reqest will fire ? How to avoid this promise gets overriden?
var infoDataPromise;
var mydata;
function getLiveData() {
infoDataPromise = $.ajax({
type: "POST",
dataType: 'JSON',
contentType: 'application/json',
url: "someUrl",
success: function (data) {mydata = data; },
error: function () {
getLiveData();
}
});
}
function bindLiveData(Id) {
infoDataPromise.done(() => {
if (mydata) {
var item = mydata.find(x => x.Id === Id);
adjustUIForId(item);
}
}).fail(() => {
mydata = null;
});
}
Don't write functions that manipulate globals.
Return promises from functions instead
Use async/await syntax to make promises easier to manage
Use recursion to handle your retry attempts
const getLiveData = async () => {
const config = {
type: "POST",
dataType: 'JSON',
contentType: 'application/json',
url: "someUrl"
};
try {
return await $.ajax(config);
} catch (e) {
console.log(e);
return getLiveData();
}
}
const handleData = async () => {
const data = await getLiveData();
if (!data) return;
const item = data.find(x => x.Id === Id);
adjustUIForId(item);
};
handleData();
Now getLiveData returns a single promise that resolves when there is a successful request and you don't need to worry about any other promises. There's no overwriting going on.
I am trying to call a odata end Point and check the response and make a another call to different endpoint if the condition is not met. If I don’t have the condition and making just one call to just one end point it works, but below code is throwing Reference error even with the attempt to the first call
$scope.getRequest = function () {
var url = $rootScope.BaseURL;
var config = {
headers: {
'Authorization': `Basic ${$scope.key}`,
'Prefer': 'odata.maxpagesize=2000'
}
};
$http.get(url, config)
.then(
function (response) { // success async
$scope.viewRequest.data = response.data;
},
function (response) { // failure async
console.log("There was an error getting the request from CORE");
}
);
if (viewRequest.data.REV_SAMPLE_CMQREQUEST.length = 0) {
var url = $rootScope.BaseURL + `CMQ_REQUEST('${$scope.viewRequest.barcode}’)`;
var config = {
headers: {
'Authorization': `Basic ${$scope.key}`,
'Prefer': 'odata.maxpagesize=2000'
}
};
$http.get(url, config)
.then(
function (response1) { // success async
$scope.viewRequest1.data = response1.data;
},
function (response1) { // failure async
console.log("There was an error getting the request from CORE");
}
);
}
};
Below is the screenshot of the error
$scope.getRequest = function () {
var url = $rootScope.BaseURL;
var config = {
headers: {
'Authorization': `Basic ${$scope.key}`,
'Prefer': 'odata.maxpagesize=2000'
}
};
$http.get(url, config)
.then(function (response) { // success async
$scope.viewRequest.data = response.data;
},
function (response) { // failure async
console.log("There was an error getting the request from CORE");
}
)
.then(nextViewRequest);
};
var newViewRequest = function (response) {
var url1 = $rootScope.BaseURL + `CMQ_REQUEST('${$scope.viewRequest.barcode}')`;
if ($scope.viewRequest.data.REV_SAMPLE_CMQREQUEST.length = 0) {
return $http.get(url1, config)
.then(
function (response1) { // success async
$scope.viewRequest1.data = response1.data;
},
function (response1) { // failure async
console.log("There was an error getting the request from CORE");
}
);
}
return $q.reject({ message: 'Validations didnt work' });
};
You are making 2 request in parallel rather than wait for the first one to finish and then make the second one, also the code is hard to read. My guess is that the second response is not returning anything because the first condition is not met.
I recommend you to read about promises chaining and the $q service to make custom rejections or resolve promises in your scenarios to order this logic your code should like something like this:
$scope.getRequest = function () {
// setup url and config
$http.get(url, config)
.then(nextViewRequest) // the return of this function will override the next result of the next promise chaining
.then(function(response) {
$scope.viewRequest1.data = response.data;
});
};
var nextViewRequest= function(response) {
// validations necessary
if(valid) {
return $http.get(url, config);
}
// If conditions are not met, then you can use the $q service to create a rejection
return $q.reject({message: 'validations on second request failed'});
};
I have a form which is submitting through javascript. on form submits I call following function to run ajax request in loop to update all versions, I want to redirect to another url when all versions are updated. I am trying to use promise resolve method but not sure how can it be used with ajax and ajax in loop, following is my code.
<script>
$(document).ready(function(){
$('#frm').submit(function(e){
e.preventDefault();
var fields = $(this).serialize();
var a = $(this);
$.ajax({
type: "POST",
url: "",
data: fields,
success: function(res) {
console.log(res)
count += updateVersion(fields,a);
},
error: function(res) {
console.log(res)
}
});
console.log('count',count);
})
});
</script>
After success I am calling a method "updateVersion" in which I have used ajax to update json for multiple versions.
function updateVersion(fields,e){
var versions = ["v5","v6","v7","v8","v9","v4"];
const promise = new Promise((resolve, reject) => {
$.each(versions, function(key, v) {
$.ajax({
type: "POST",
url: "",
data: e.serialize()+ "&update_json=1&version="+v,
success: function(res) {
console.log(res)
status = 1;
},
error: function(res) {
console.log(res)
}
});
});
resolve(window.location.href= "https://www.example.com");
});
promise.then((successMessage) => {
console.log('Got data! Promise fulfilled.');
}, error => {
console.log('Promise rejected.');
console.log(error.message);
});
}
Any help will be appreciated.
Thanks!
I have the following code which basically does the following:
When the user clicks the button, an encoded text is sent to an API which takes care to decode it. Then the decoded text is set as a query on a Google link which is opened on a new tab.
JAVASCRIPT
// Reference:
// https://base62.io/
let message_encoded = 'T8dgcjRGkZ3aysdN'; // base62('Hello World!'')
let way = 1;
$(function () {
$('.button_test').click(async function() {
let url_api = 'https://api.base62.io/decode';
let response;
switch (way) {
case 1: /* this works */
response = await jQuerySyncPost(url_api, { data: message_encoded });
break;
case 2: /* this DOES NOT work */
response = await fetchSyncPost(url_api, { data: message_encoded });
break;
case 3: /* this DOES NOT work */
response = await axios.post(url_api, { data: message_encoded });
break;
}
alert('Next, let\'s look on Google for: "' + response.data.decoded + '"');
let message_decoded = response.data.decoded;
let url_google = 'https://www.google.com/search?q='+encodeURIComponent(message_decoded);
openNewTab(url_google);
});
});
function jQuerySyncPost(url, params) {
return new Promise((resolve, reject) => {
$.ajax({
type: 'POST',
url: url,
data: params,
async: false,
success: function (response) {
response = {
data: response
};
resolve(response);
},
error: function (jqXHR, textStatus, errorThrown) {
reject({
jqXHR: jqXHR,
textStatus: textStatus,
errorThrown: errorThrown
});
}
});
});
}
function fetchSyncPost(url, params) {
return new Promise((resolve, reject) => {
let config = {
headers: {
'content-type': 'application/json; charset=UTF-8',
},
body: JSON.stringify(params),
method: 'POST',
};
fetch(url, config)
.then(response => response.json())
.then(response => {
let result = {
data: response,
};
resolve(result);
});
});
}
function openNewTab(url) {
window.open(url, '_blank');
}
HTML
<button class="button_test">Click Here</button>
CSS
.invisible {
visibility: hidden;
}
.button_test {
margin-left: 200px;
margin-top: 100px;
font-size: 50px;
}
The code above is working fine with the jQuery Ajax call (through custom function):
let response = await syncPost(url_api, { data: message_encoded });
But, in the other hand, if I use axios, then I get a popup blocked alert on the browser which prevents the new tab to get opened:
let response = await axios.post(url_api, { data: message_encoded });
Here you have the JSFiddle: https://jsfiddle.net/otyzadju
where { way=1 } works, but { way=2, way=3 } don't.
On both cases the call to the API via Ajax is done synchronously but the problem is that it seems that axios is disconnecting what happens next from user's action when called after clicking a button.
Any idea on how to make this work with axios?
If possible, please, provide your solution on a forked JSFiddle link.
Thanks!
I changed the answer to window.open so it will work with axios.post - it also works with fetch. If you didn't use this originally for a certain reason then please let me know and I can try and update the answer.
function openNewTab(url) {
window.open(url, '_blank')
}
This is a JSfiddle fork with it set to option 2 and the opennewtab function altered.
https://jsfiddle.net/xuywfqzn/
Is there a way to ignore or bypass this error code 400 and just trigger a function when this error occurs?
I tried this : --
vm.createOrder = function () {
var deferred = $q.defer(); // update
orderService.createOrder()
.then(function (response, status) {
deferred.resolve(response); // update
if(status == 400) {
console.log('Should never happen but trigger a function anyway! ');
localStorage.clear();
}
console.log('Successfully created an order: ', response.status);
})
}
orderService: --
createOrder: function () {
return $http({
url: 'apiURL',
method: 'POST'
})
}
but it doesn't even console.log the string in the if(status) condition or in the success, but the POST method does go through so its posting the data I want to post but it returns an error code of 400.
Edit fixed
orderService.createOrder()
.then(function (response) {
console.log('Successfully created an order: ', response.status);
})
.catch(function (e) {
console.log('Should never happen but trigger a function anyway! ');
localStorage.clear();
console.log(e);
})
Your service should look like this.
createOrder: function () {
return $http.post('SomeURL')
.then(function (response) {
return response.data;
},
function (response) {
return $q.reject(response);
});
}
You should call it like this, rather than a try catch.
orderService.createOrder().then(function(response){
//Successfull Call
},function(response){
//Error on call
});