How to launch 1 by 1 ajax request - javascript

I'm having some trouble. I'm trying to execute my ajax function 1 by 1, not all at the same time. I'm using promise but I have no more idea on how to achieve it. Here is my code :
function run_action(action){
if(action == "login"){
return $.ajax({
url: "login.php",
type: "post",
data: {password: password},
beforeSend: function() {
console.log('beforeSend login');
},
success: function (response) {
console.log('Success Login');
},
error: function (request, error) {
console.log('Error Login');
},
})
}
if(action == "register"){
return $.ajax({
url: "register.php",
type: "post",
data: {password: password},
beforeSend: function() {
console.log('beforeSend register');
},
success: function (response) {
console.log('Success Register');
},
error: function (request, error) {
console.log('Error Register');
},
})
}
}
var actions = ['register', 'login'];
services.forEach(checkActions);
function checkActions(item, index) {
if (document.getElementById(item).checked) {
var promise = run_action(item);
promise.success(function (data) {
console.log('Run after');
});
console.log('Run first')
}
}
In this case login and register are both launched at the same time, login doesn't wait for register to finish so he can start processing.

In case you can't properly wait for checkActions from the outside, you could maintain a task queue for that:
let queue = Promise.resolve();
function checkActions(item, index) {
queue = queue
.then(() => run_action(item))
.then(() => {
console.log("Next item was processed", item);
// Your code here
});
// Synchronous code here - This won't execute in order!
}

Currently your code runs through the forEach loop with each action and invokes checkActions with that action, thus firing the request. Array.prototype.forEach executes synchronously (without any kind of check to the promises returned by $.ajax). The following would wait for 'register' to finish before firing 'login':
function checkActions(item) {
if (document.getElementById(item).checked) {
return run_action(item);
}
}
checkActions('register')
.then(data => {
return checkActions('login');
});
I'm not super familiar with jQuery's promise structure, so I used .then, but I believe you can use .success without issue as well.
Unrelated comment, your run_actions function should really be split into two functions (login and register) since they are completely unrelated aside from the fact that they are making requests.

First- its not a good practice to trust a order-based function (AKA - run them by the array order), run your functions according to logic.
For example: if the first function was failed - you dont want to run the next functions in the array.
If you consist to run the functions in array - you can use an async
async function runActions( actionsList ) {
for(const action of actionsList) {
await run_action( action );
}
};
In general - we use the then method to run anther function when specific promise is done. Like so:
promise.then( onSuccess => {
// try to log in
}, onFail => {
// tell the user the signup was failed
});
BTW - you can use the native fetch instade of jQuery ajax, and get simple to use, promise-based way to communicate with your sever.
Like so:
fetch("login.php" , {
method: 'POST', // or 'PUT'
body: {password: password}, // data can be `string` or {object}!
headers:{
'Content-Type': 'application/json'
}
}).then( ... )

Related

how to do ajax call recursively, to check if service is available

I am trying to do ajax call recursively to check whether web service is available or not.
Here is my code
function checkWebService() {
console.log("function called")
$.ajax({
url: "localhost:8085/login/checkWebServiceIsRunning",
type: 'GET',
success: (response) => {
console.log("success")
},
error: (jqXHR, textStatus, errorThrown) => {
console.log("error")
setTimeout(() => {
checkWebService()
}, 1000)
}
})
}
As my service is not running initially, the code from error block gets executed but after two or three times, the ajax call gets stuck, it neither goes in success nor in error.
Then I tried putting timeout in ajax code, so it gave timeout error, but even if the web service is available and server side code gets executed without any delay, ajax gives timeout issue.
Can someone help on what could be going wrong here.
You can use a while loop and async/await to get the response.
Also, you can add a variable count for the attempt to prevent infinite loop.
Furthermore, a wait function is added to delay each call to not utilize the program.
Note: errorCode:789 is just an example to catch the max attempt error. you can define your own.
const wait = ms => new Promise((resolve, reject) => setTimeout(resolve, ms));
async function checkWebService() {
const MAX_ATTEMPT = 20;
let attempt = 0;
while (true) {
try {
attempt++;
if (attempt > MAX_ATTEMPT) throw { errorCode: "789" };
const response = await $.ajax({
url: "localhost:8085/login/checkWebServiceIsRunning",
type: "GET",
});
return response;
} catch (err) {
if (err.errorCode === "789") {
throw new Error("Exceed Max. attempt.");
}
console.log(err);
await wait(1000);
continue;
}
}
}
checkWebService().then(console.log).catch(console.log);

calling a method once the data is appended after executing the ajax calls

I have an array of table names, so I need to fetch response of three tables. See the below code. Once the data is appended to the dom I need to call the successMessage method, now I am using setTimeout how can I use promise in the this scenario
let lists = ['table1', 'table2', 'table3']
lists.map(list => {
$.ajax({
url:`${rootUrl}/api/('${list}')`,
type: 'GET',
headers: {
accept: 'application/json'
},
success: res => dataDisplay(res),
error: err => console.log(JSON.stringify(err))
})
})
// displaying data
const dataDisplay = (res) => {
switch(res.TableName){
case 'Table1':
$("#tbl1 p").text(res.TableOriginalName)
$("#tbl1 .content p").text(res.TableDescription)
break;
case 'Table2':
$("#tbl2 p").text(res.TableOriginalName)
$("#tbl2 .content p").text(res.TableDescription)
break;
case 'Table3':
$("#tbl3 p").text(res.TableOriginalName)
$("#tbl3 .content p").text(res.TableDescription)
break;
default:
return
}
}
// successfully data appended
const successMessage = () => alert("data appended successfully")
// calling the success method once data is appended
setTimeout(successMessage, 3000)
You'd use Promise.all to wait for all of those requests to finish before showing the message. First, build up an array of the promises:
var promises = lists.map(list => $.ajax({
url:`${rootUrl}/api/('${list}')`,
type: 'GET',
headers: {
accept: 'application/json'
},
success: res => dataDisplay(res),
error: err => console.log(JSON.stringify(err))
}));
then wait for them to complete
Promise.all(promises).then(() => alert("data appended successfully"));
You can also use $.when for much the same purpose, but it's awkward to call:
$.when.apply($, promises).done(() => ...);
In comments you've said that dataDisplay loads a bunch of images and you need to delay the call to successMessage until after those images have loaded. To do that, you'll need to watch for the load event on the images. This can be a bit squirrelly because the images can load before you hook the event, so we'll want to use the image's complete flag as well. Something along these lines:
Promises.all(/*...*/).then(() => {
// Get all images in the tables we added to
let imgs = $("#tbl1 img, #tbl2 img, #tbl3 img");
// Hook up a function to check for completed images when we
// see a `load` event on any of them, and fire that proactively
imgs.on("load", checkComplete);
checkComplete();
function checkComplete() {
// Count any incomplete images; remove the handler from any
// complete image and remove it from our `imgs` set
let incomplete = 0;
imgs.get().forEach(img => {
if (img.complete || img.error) {
$(img).off("load", checkComplete);
imgs = imgs.not(img);
} else {
++incomplete;
}
});
if (incomplete == 0) {
// They're all done!
successMessage();
}
}
});
That's off the top of my head, may need some tweaking, but it should get you headed the right way.
You may try this code:
let tableNames = ['table1', 'table2', 'table3']
let promiseArr = tableNames.map((table) => {
return new Promise((resolve, reject) => {
$.ajax({
url:`${rootUrl}/api/('${list}')`,
type: 'GET',
headers: {
accept: 'application/json'
},
success: (res) => {
dataDisplay(res);
resolve(table);
},
error: (err) => {
console.log(JSON.stringify(err));
reject(table);
}
});
}).catch((e) => {
// if call to any of the table's url fails
// it will come here, var 'e' contains
// that table name, handle here
console.log(e + " has fails"); return e;
});
});
Promise.all(promiseArr).
then((result) => {
// this will run regardless of, if call to any table fails
console.log("success")
})
.catch((result) => {
console.log(result + " fails");
});
this will asynchronously call the tables, and at the end comes to Promise.all()'s then() even if call to some table fails

How to execute AJAX calls in order in loop using Javascript Promise

I am looping through a map, where I want to make a separate AJAX call with each map value as parameter, to fetch some data and log it. See below. This is working, but I'd like to have the AJAX calls go in order of the map. Because each call is asynchronous, so seems like I should use promises to achieve execution in order. But I am new to promises and don't really know how to do it here. I have look elsewhere on here but could not find anything. Please help.
map.forEach(function(url, key) {
log(url);
});
function log(url) {
$.ajax({
url: url,
dataType: 'json',
success: function (result) {
console.log(result.value);
console.log(result.name);
console.log(result.action);
}
});
}
Since $.ajax returns a promise, you can use promise chaining to achieve what you want
var p = $.when();
map.forEach(function(url, key) {
p = p.then(function() {
return log(url);
});
});
function log(url) {
return $.ajax({
url: url,
dataType: 'json',
success: function (result) {
console.log(result.value);
console.log(result.name);
console.log(result.action);
}
});
}
Note: the code above uses only jQuery, no native promises
Or using reduce function of Array
map.reduce(function(p, url) {
return p.then(function() {
return log(url);
});
}, $.when());
If you can use ES2015+, so have native Promises,
map.reduce((p, url) => p.then(() => log(url)), Promise.resolve());
If you wanted, you can also do it like this
function log(url) {
return $.ajax({
url: url,
dataType: 'json'
});
}
map.reduce((p, url) => p.then(results => log(url).then(result => results.concat(result))), Promise.resolve([]))
.then(results => {
results.forEach(result => {
console.log(result.value);
console.log(result.name);
console.log(result.action);
})
});
The difference being as that all the console.log's would happen once the LAST request finished (and if any fail, none of the console log's would happen)
If you could use async/await syntax in your project, then nothing could be easier:
async function log(url) {
return await
$.ajax({
url: url,
dataType: 'json',
})
.then(function(result) {
console.log(result.value);
console.log(result.name);
console.log(result.action);
});
}
async function run() {
for (var i = 0; i < map.length; i++) {
await log(map[i]);
}
}
run();
You see, I changed forEach to for loop. It's important for await usage, because it provides (instead of forEach and other callback based loops) synchronicity of async log calls.
UPD Here is the Plunker which demonstrates such an approach.
To chain promises should work:
function ajaxPromises(urls) {
return Promise.all(urls.map(function(url) {
return $.ajax({
url: url,
dataType: 'json'
})
}))
}
Usage:
ajaxPromises(['http://yoururl.com','http://yoururl.com'])
.then(function (result) {
// do something with result
})
.catch(function (error) {
// do something with error
})

another function runs before end of jquery ajax request in contempt of the app order

I'd like in the first function to get response from api and put it into the object field and then in the second function log it.
App starts, when we call init function - initApp(), which describes the same order: first get data, then log it.
class App {
constructor() {
this.data;
}
// init app
initApp() {
this.getServerData();
this.foo();
}
// get data using ajax
getServerData() {
$.ajax({
url: 'https://jsonplaceholder.typicode.com' + '/posts/1',
method: 'GET',
dataType: 'json',
})
.then(data => {
// putting data to object field
this.data = data;
})
.catch(error => {
console.log(error);
});
}
// log it
foo() {
console.log('another function in action, api response:');
console.log(this.data);
}
}
const app = new App();
app.initApp();
But the field app.data is undefined, cause foo() runs before getData() ends.
It's probably to solve adding setTimeot() to initApp()
// init app
initApp() {
this.getData();
setTimeout(() => {
this.foo();
},
1000
);
}
Is it normal or it would be better to use other cases?
Problem is that in my real app besides one foo() will be another 5-7 functions. And put all of them into .then ?
Maybe there are other solutions?
Any help will be highly appreciated.
You need to use callback here.
// init app
var self=this;
initApp() {
this.getServerData(function(){ self.foo(); });
}
// get data using ajax
getServerData(callback) {
$.ajax({
url: 'https://jsonplaceholder.typicode.com' + '/posts/1',
method: 'GET',
dataType: 'json',
})
.then(data => {
// putting data to object field
this.data = data;
callback() ;
})
.catch(error => {
console.log(error);
});
}
call foo() from response of ajax call, this will make sure that foo will be executed only after ajax call.

Function to return a global variable

I feel like this is really easy, but I can not figure it out.
I want to set the currentUserId inside of the getCurrentUser function, which I would like to be able to call inside other functions.
Below is what I have now, and it returns undefined. What am I missing?
var currentUserId;
function getCurrentUser() {
$.ajax({
type: "GET",
url: '/set_user',
success: function(result) {
currentUserId = result.id;
return currentUserId;
},
error: function(err) {
console.log(err);
}
})
};
getCurrentUser();
console.log("current user id is " + currentUserId);
This happens because inside getCurrentUser method you are doing an asynchronous AJAX call, so the value is not yet ready when you print it with console.log.
The value will be correctly set when the GET /set_user request will end successfully, only in that case the function:
success: function(result) {
currentUserId = result.id;
return currentUserId;
}
will be executed and currentUserId will be set.
Based on jQuery.ajax() documentation, the value returned by $.ajax call is a Promise. So first, return the promise to the caller (1) and then wait the promise is resolved to print the value (2).
var currentUserId;
function getCurrentUser() {
return $.ajax({ // 1. Return the Promise here
type: "GET",
url: '/set_user',
success: function(result) {
currentUserId = result.id;
return currentUserId;
},
error: function(err) {
console.log(err);
}
})
};
// 2. Then wait the call to succeed before print the value (use the 'done' method)
getCurrentUser().done(function() {
console.log("current user id is " + currentUserId);
});
Like Andrea explain, the value was not ready yet when you make a ajax call.
One way to avoid this is use callback:
function getCurrentUser(callback) {
$.ajax({
type: "GET",
url: '/set_user',
success: function(result) {
var currentUserId = result.id;
if (callback)
callback(currentUserId);
},
error: function(err) {
console.log(err);
}
})
};
function displayResult(userId){
console.log("current user id is " + userId);
}
getCurrentUser(displayResult);
And this will also avoid the use of globe variable currentUserId.

Categories