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.
Related
I have a forward navigation function, which triggers an async API call on certain stages, where it is required that it does not proceed (move forward) until said API call is finished (it triggers a processor in the background required for the following stages).
My issue is that it continues to proceed, despite the call not yet being completed. I'm confused at to the best and most suggested way to implement this, and would be happy for any advice.
Code:
async postData(url = "", data = {}) {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
},
forwardTicketId(ticketId) {
const origin = new URL(window.location.href).origin;
const addr = `${origin}/current_ticket_id`;
this.postData(`${addr}`, {
data: ticketId,
});
},
goNext() {
if (this.isRequiredStage) { # this needs to complete before anything else runs
this.forwardTicketId(this.ticketId);
}
this.navGoNext();
},
How the goNext function is called:
<div class="level-right">
<TicketStepsNavButtons
#goPrev="goPrev"
#goNext="goNext"
/>
</div>
Use await in the calls too.
async postData(url = "", data = {}) {
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
// you can return the response if you need it
return response;
},
forwardTicketId(ticketId) {
const origin = new URL(window.location.href).origin;
const addr = `${origin}/current_ticket_id`;
// return to chain the next promises, you can async/await instead if you don't care about the result
return this.postData(`${addr}`, {
data: ticketId,
});
},
async goNext() {
if (this.isRequiredStage) { // this needs to complete before anything else runs
// await here
await this.forwardTicketId(this.ticketId);
}
this.navGoNext();
},
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!
As the title says what I was trying to do is make a universal function to both do GET and POST calls with one function. However, because when sending a GET call it requires the params entry to contain the data, when sending data via POST it requires the data entry (if I'm not mistaken).
I currently have the following function;
function api(method, call, params){
return new Promise(function(resolve, reject){
axios({
url: call,
method,
params
}).then(function(response) {
var body = response.data;
if(body.status !== 1){
return reject(body.message);
}
resolve(body.response);
}).catch(function(err){
reject(err);
});
});
GET calls work fine as there is the params entry, but for POST calls it stops working. How can I fix this so I have one function to handle both calls?
Another way would be to accept config object as a parameter. Also, you do not need to wrap axios() in a new promiseasaxios()` returns a promise itsef.
function api(config) {
const baseUrl = "http://foo.baz";
const updatedConfig = Object.assign({}, config, {
// If you want to append base url to all api methods.
url: `${baseUrl}${config.url}`
});
return axios(updatedConfig).then(response => {
const body = response.data;
if (body.status !== 1) {
throw new Error(body.message);
}
return body.response;
});
}
Usage:
api({
url: "/user",
method: "get",
params: { id: "xyz" }
}).then(() => { });
or
api({
url: "/tag",
method: "post",
data: { tag: "abc" }
}).then(() => { });
I solved it by just pulling the object into a variable and adding the entry.
Example:
var data = {
url: call,
method
}
if(method.toLowerCase() === 'post'){
data['data'] = params;
}else{
data['params'] = params;
}
axios(data).then(function(response) ...
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.