How can I wait until both ajax requests done? - javascript

Here is a simplified of my code:
var res = array();
$.ajax({
url: 'test1.php',
async: true,
success: function (data) {
res[1] = data.result;
}
});
$.ajax({
url: 'test2.php',
async: true,
success: function (data) {
res[2] = data.result;
}
});
if ( /* both ajax request are done */ ) {
// do stuff
} else {
// wait
}
As you can see I've used async: true to run those ajax requests at the same time (in parallel). Now I need to wait until both requests done. How can I determine an ajax request is done? If not wait until it get done?

You can use promises:
Promise.all([
$.ajax({ url: 'test1.php' }),
$.ajax({ url: 'test2.php' })
])
.then(([res1, res2]) => {
// Both requests resolved
})
.catch(error => {
// Something went wrong
});

You can use callback function as well.
var res = [];
function addResults(data) {
res.push(data);
console.log('Request # '+res.length);
if ( res.length >= 2 ) {
// do stuff
console.log('both request has done.');
} else {
// wait
}
}
$.ajax({
url: 'https://jsonplaceholder.typicode.com/posts',
success: function (data) {
addResults(data);
}
});
$.ajax({
url: 'https://jsonplaceholder.typicode.com/posts',
success: function (data) {
addResults(data);
}
});

Use Promise.all function. It will be resolved if all of the promises is resolved and pass the data as array to the then function, else it will be rejected with the first promise failure value
Promise.all([
$.ajax({ url: 'test1.php'}),
$.ajax({ url: 'test2.php'})
])
.then(results => {
// results is an array which contains each promise's resolved value in the call
})
.catch(error => {
// The value of the first rejected promise
});

this official document could help you.
http://api.jquery.com/ajaxStop/
example:
var res = [];
$.ajax({
url: 'test1.php',
async: true,
success: function (data) {
res.push('Ajax one is complete');
}
});
$.ajax({
url: 'test2.php',
async: true,
success: function (data) {
res.push('Ajax two is complete');
}
});
var resALL = function(){
console.log(this)
}
//After the requests all complete
$(document).ajaxStop(resALL.bind(res))

Related

Javascript await is only valid in async functions

I have this function to delete items once a popup returns true:
function deleteItems(selectedItems){
if (selectedItems.length > 0) {
$("#confirm-popup-modal").modal("show");
$("#confirm-popup-modal").one('hidden.bs.modal', function (event) {
if ($("#confirm-modal").val() == "true") {
var form_data = selectedItems;
$.ajax({
url: "#Url.Action("Delete", #ViewContext.RouteData.Values["controller"].ToString())",
method: "POST",
data: JSON.stringify(form_data),
contentType: "application/json",
success: function (result) {
if (result.Result == true) {
var deleteId = result.Output;
await CompletedJobsAccess(deleteId);
table.draw();
}
},
error: function (error) {
console.log(error);
}
});
}
});
}
}
Inside the Ajax success is another function called CompletedJobsAccess that will keep looping every 3 seconds to check if a job deletion has been completed:
function CompletedJobsAccess(DeleteId){
return new Promise((resolve,reject)=>{
var loopInterval = setInterval(function() {
$.ajax({
url: "#Url.Action("Verify", "CompletedJobsAccess", new {area="Base" })",
method: "POST",
data: JSON.stringify(DeleteId),
contentType: "application/json",
success: function(verifyResult) {
if (verifyResult.IS_COMPLETED == true && verifyResult.IS_PROCESSING == false) {
if (verifyResult.IS_SUCCESSFUL == true) {
console.log(verifyResult.OUTPUT);
$.each($.parseJSON(verifyResult.OUTPUT), function(index, value) {
if (value.Result == true) {
toastr.success(value.Message);
}else{
toastr.error(value.Message);
}
});
clearInterval(loopInterval);
} else {
toastr.error(verifyResult.ERROR_MESSAGE);
}
}
},
error: function(innerError) {
console.log(innerError);
}
});
}, 3000);
});
}
However, when I load the page, and call deleteItems(selected);, this is the error I get:
Uncaught SyntaxError: await is only valid in async functions and the
top level bodies of modules
I tried searching around but I can't find if it can work within an ajax success function.
EDIT:
Added async to the ajax success function but the table draw function doesn't run.
function deleteItems(selectedItems){
if (selectedItems.length > 0) {
$("#confirm-popup-modal").modal("show");
$("#confirm-popup-modal").one('hidden.bs.modal', function (event) {
if ($("#confirm-modal").val() == "true") {
var form_data = selectedItems;
$.ajax({
url: "#Url.Action("Delete", #ViewContext.RouteData.Values["controller"].ToString())",
method: "POST",
data: JSON.stringify(form_data),
contentType: "application/json",
success: async function (result) {
if (result.Result == true) {
var deleteId = result.Output;
console.log("table before");
await CompletedJobsAccess(deleteId);
console.log("table draw");
table.draw();
}
table.draw();
},
error: function (error) {
console.log(error);
}
});
}
});
}
}
EDIT 2: Updated CompletedJobsAccess to resolve promises:
function CompletedJobsAccess(DeleteId){
return new Promise((resolve,reject)=>{
var loopInterval = setInterval(function() {
$.ajax({
url: "#Url.Action("Verify", "CompletedJobsAccess", new {area="Base" })",
method: "POST",
data: JSON.stringify(DeleteId),
contentType: "application/json",
success: function(verifyResult) {
if (verifyResult.IS_COMPLETED == true && verifyResult.IS_PROCESSING == false) {
if (verifyResult.IS_SUCCESSFUL == true) {
console.log(verifyResult.OUTPUT);
$.each($.parseJSON(verifyResult.OUTPUT), function(index, value) {
if (value.Result == true) {
toastr.success(value.Message);
}else{
toastr.error(value.Message);
}
});
clearInterval(loopInterval);
return Promise.resolve();
} else {
toastr.error(verifyResult.ERROR_MESSAGE);
return Promise.resolve();
}
}
},
error: function(innerError) {
console.log(innerError);
}
});
}, 3000);
});
}
Just make the success function async
$.ajax({
url: "https://jsonplaceholder.typicode.com/users/3",
method: "GET",
success: async function(data) {
console.log("first - now wait a second ...");
await new Promise((res) => setTimeout(res, 1000));
console.log("second, data:",data);
},
error: function(innerError) {
console.log(innerError);
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Working JSFiddle (can't work on this site because of CORS)
In CompletedJobsAccess(DeleteId) you return a promise. But the way you set it up it will never execute the resolve function. So your await will wait forever ...
You could place the line
resolve();
right after
clearInterval(loopInterval);
in your CompletedJobsAccess function to make it work.
Do not return yet another Promise.resolve() like you did in your edited code.
A resolve function for a promise is never returned but executed.
Try Adding async before all the function keyword like async function deleteItems(selectedItems){ and also $("#confirm-popup-modal").one('hidden.bs.modal', async function (event) { and it should do the job.
You're using await in functions that don't use the async keyword. await isn't available in regular functions. To solve this, you can change all the functions using await to async function to make it into an asynchronous function.
And if you don't want want to go through every function to make it asynchronous, you can just put the entire code inside an asynchronous IIFE

JavaScript - Wait until multiple ajax requests are done then do something

I have two ajax calls in JavaScript that add content to the page asynchronously. I need to create a "timeout" function (or something similar) that waits until they're both done loading to then execute more code. Here is what I have so far
https://jsfiddle.net/qcu5asnj/
<div class='resultsSection'>
Example
</div>
<script>
var loading_first = "loading";
var first_ajax = {
url: 'https://example.com/load.php?q=fast',
type: "GET",
success: function( data ) {
console.log("success");
$(".resultsSection").append(data);
loading_first = "done";
}
};
$.ajax( first_ajax );
var loading_second = "loading";
var second_ajax = {
url: 'https://example.com/load.php?q=slow',
type: "GET",
success: function( data ) {
console.log("success");
$(".resultsSection").append(data);
loading_second = "done";
}
};
$.ajax( second_ajax );
// Need to create a function that waits until both are done. I know this is wrong
if((loading_first == "done") && (loading_second == "done")){
console.log("Both done loading, execute more code here");
}
</script>
Store the promises returned by $.ajax in variables and use Promise.all()
const req1 = $.ajax('https://jsonplaceholder.typicode.com/todos/1').then(data => {
console.log('First request done')
return data
})
const req2 = $.ajax('https://jsonplaceholder.typicode.com/todos/2').then(data => {
console.log('Second request done')
return data
})
Promise.all([req1, req2]).then(results=>{
console.log('All done')
console.log('Combined results',results)
}).catch(err=> console.log('Ooops one of the requests failed'))
.as-console-wrapper {max-height: 100%!important;top:0;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Js Promise for success and error for ajax

I wrote this piece of code where I get the Json from an ajax call. I need to handle the response (success, error) with javascript promise (resolved after 2 seconds) and .then() method. I read a few stuff online but don't know where to begin. Can anybody help me please? Thanks
function jsonResult(spaceName){
var baseUrl = "BaseUrl";
$.ajax({
url:baseUrl + "/api/request/url",
type:"GET",
dataType: "json",
error: function(xhr,status,error) {
console.log(JSON.stringify(error));
},
success: function(response){
getResult(response)
}
});
}
You just need to update your function to return a promise and then in your success and error methods just call resolve and reject respectively. Here is a sample pulled from MDN:
function myAsyncFunction(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(xhr.statusText);
xhr.send();
});
}
You can read more on this here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
Here is a simple implementation of your example code:
function jsonResult(spaceName){
var baseUrl = "BaseUrl";
return new Promise((resolve, reject) => {
$.ajax({
url:baseUrl + "/api/request/url",
type:"GET",
dataType: "json",
error: function(xhr,status,error) {
reject(JSON.stringify(error));
},
success: function(response){
resolve(response);
}
});
});
}
You can user return a promise on calling this function which will resolve in success callbacka and will be rejected in error callback like this
function jsonResult(spaceName){
var baseUrl = "BaseUrl";
return new Promise((resolve, reject) => {
$.ajax({
url:baseUrl + "/api/request/url",
type:"GET",
dataType: "json",
error: function(xhr,status,error) {
console.log(JSON.stringify(error));
reject(error);
},
success: function(response){
resolve(getResult(response));
}
});
}
}
// Usage
jsonResult(someInput).then(response => {
// success
})
.catch(error => {
// error
});
jQuery 3.0 is Promises/A+ compatible so just remove the error/success and do the then/catch
function jsonResult(spaceName) {
var baseUrl = "BaseUrl";
$.ajax({
url:baseUrl + "/api/request/url",
type:"GET",
dataType: "json"
}).then(console.log, console.error)
}
alternativ is the the native fetch api...
fetch(baseUrl + "/api/request/url")
.then(res => res.json())
.then(console.log)

Using jQuery when to defer ajax processing

I have a list of 15+ ajax requests that need to be called in a specific order. I need each ajax call to wait until the previous function finishes before making the next call. This issue arises because my ajax call, has a direct callback that is also an ajax call.
createCheckIn() {
this.selectedList = [...] // long list of objects
count = 0
for ( i=0; i < this.selectedList.length; i++ ) {
$.ajax({
method: "POST",
url: url,
data: {
check_in: {
client_id: this.selectClient.id,
program_id: this.program_id
}
},
success: function(res) {
that.createWeighIn(count, res.id)
count = count + 1
},
error: function(err) {
console.log(err)
}
})
}
},
createWeighIn(index, check_in_id) {
let data = {}
let that = this
data.weigh_in = this.selectedList[index]
$.ajax({
method: "POST",
url: url,
data: data,
success: function(res) {
console.log(res)
},
error: function(err) {
console.log(err)
}
})
}
the correct data is generated but I believe the ordering is off because eventually there is a call to createCheckIn() that begins before the previous entry has completed.
Is there a way to chain these functions such that createCheckIn() and createWeighIn() are called (and complete) before selectedList iterates.
your for loop in createCheckIn() will not stop to wait on your ajax return. you can do something like:
function createCheckIn(oldI, oldCount){
var count = 0;
var currentI = 0;
if(oldCount != null){
count = oldCount;
}
if(oldI != null){
currentI = oldI;
}
if(currentI < this.selectedList.length){
$.ajax({
method: "POST",
url: url,
data: {
check_in: {
client_id: this.selectClient.id,
program_id: this.program_id
}
},
success: function(res) {
that.createWeighIn(count, res.id)
createCheckIn(currentI + 1, count + 1);
},
error: function(err) {
console.log(err)
}
}); //ajax
} // if
}
seems likely that you can eliminate one of those counters too, the i or the count
Seems like this is missing some potentially really important details about what you need to do leading up to this (ie. this.selectedItems generation) and what happens after (what if one call checkin fails, what if a checkin succeeds but its corresponding weighIn fails, etc..). That said...
It seems you are not actually using the counter for anything other than to reference data you already have, so why not just pass that in directly like:
createWeighIn(weighInData, check_in_id) {
let data = {};
let that = this;
data.weigh_in = weighInData;
// ... your other code
}
I would make createCheckIn only handle doing the ajax request and making a single "reservation" in your system. Then i would make a new method called checkIn that uses the two previous method to process all of selected items:
checkIn() {
let self = this;
let promises = [];
let this.selectedList = [...];
for (let = 0; i < this.selectedList.length; i++) {
// always create the deferred outside the call
let def = $.Deferred();
promises.push(def.promise());
this.createCheckIn().done(function (res) {
self.createWeighIn(self.selectedList[i], res.id))
.done(function () {
// resolve
def.resolve.apply(def, Array.prototype.slice.call(arguments);
})
.fail(function () {
def.reject.apply(def, Array.prototype.slice.call(arguments);
});
}).fail(function () {
// if checkin fails, always reject because we know weighIn wont be called
def.reject.apply(def, Array.prototype.slice.call(arguments);
});
};
// this will resolve/fail when all promises (from createWeighIn) resolve/fail
return $.when.apply(null, promises);
}
so putting it all together:
{
createCheckIn() {
let request = $.ajax({
method: "POST",
url: url,
data: {
check_in: {
client_id: this.selectClient.id,
program_id: this.program_id
}
}
})
.fail(function(err) {
console.log(err)
});
};
return request;
},
createWeighIn(data, check_in_id) {
let params = {};
params.weigh_in = data;
let request = $.ajax({
method: "POST",
url: url,
data: params,
success: function(res) {
console.log(res)
},
error: function(err) {
console.log(err)
}
});
return request;
},
checkIn() {
let self = this;
let promises = [];
let this.selectedList = [...];
for (let = 0; i < this.selectedList.length; i++) {
// always create the deferred outside the call
let def = $.Deferred();
promises.push(def.promise());
this.createCheckIn().done(function (res) {
self.createWeighIn(self.selectedList[i], res.id))
.done(function () {
// resolve
def.resolve.apply(def, Array.prototype.slice.call(arguments);
})
.fail(function () {
def.reject.apply(def, Array.prototype.slice.call(arguments);
});
}).fail(function () {
// if checkin fails, always reject because we know weighIn wont be called
def.reject.apply(def, Array.prototype.slice.call(arguments);
});
};
// this will resolve/fail when all promises (from createWeighIn) resolve/fail
return $.when.apply(null, promises);
}
}
I ended up introducing promises, and some recursion and removing the loop altogether. I basically begin the process by calling createCheckIn() with an index of 0:
this.createCheckIn(0)
createCheckIn(index) {
this.selectedList = [...] // long list of objects
count = 0
let prom = new Promise(function(resolve, reject) {
$.ajax({
method: "POST",
url: url,
data: {
check_in: {
client_id: that.selectClient.id,
program_id: that.program_id
}
},
success: function(res) {
resolve(that.createWeighIn(index, res.id))
},
error: function(err) {
reject(console.log(err))
}
})
})
},
createWeighIn(index, check_in_id) {
let data = {}
let that = this
data.weigh_in = this.selectedList[index]
let prom = new Promise(function(resolve, reject) {
$.ajax({
method: "POST",
url: url,
data: data,
success: function(res) {
console.log(res)
if ( index == (that.selectedList.length - 1) ) {
that.complete = true
resolve(console.log("complete"))
} else {
index++
resolve(that.createCheckIn(index))
}
},
error: function(err) {
console.log(err)
}
})
})
}

how to call http call inside the for loop after success of before http call?

I want to call the api after completing the first is success.But in my code it call the api before the first one was completed
for(var w=0;w<Ids.length;w++){
$scope.msgObject = {
"SenderID":$scope.pageId,
"PageID" : $scope.pageId,
"Date":Date.now().toString(),
};
$http({
method: 'POST',
url: '///url',
async:true,
data: $scope.msgObject,
headers: {
'Content-Type': 'application/json'
}})
.then(function(response) {
console.log("success posting");
}
})
.catch(function(response){
});
$(".messageInput").val('');
}
}
}
}
function asyncForEach(arr, cb) {
return arr.reduce((p,c)=>{
return p.then(()=> cb(c));
}, Promise.resolve());
}
function fetch(id) {
return new Promise(resolve=>
setTimeout(resolve, 100)) // replace with your AJAX call
.then(()=>console.log('success posting', id));
}
function done() {
console.log('all done!');
}
const ids = [1, 2, 3, 4, 5];
asyncForEach(ids, fetch).then(done);
put your loop inside THEN
something like
function urPostMethod(url){
$scope.msgObject = {
"SenderID":$scope.pageId,
"PageID" : $scope.pageId,
"Date":Date.now().toString(),
};
$http({
method: 'POST',
url: url,
async:true,
data: $scope.msgObject,
headers: {
'Content-Type': 'application/json'
}})
.then(function(response) {
console.log("success posting");
while(Ids.length>0)urPostMethod(Ids.pop());
}
})
.catch(function(response){
});
$(".messageInput").val('');
}
}
}
}
What you are trying to do is to mix async and sync operations together which is not logical at all.
If you need to call those API's in order of your elements in your array, you may use a different approach like pipe the requests using Defer:
var dfd = $.Deferred(),
x = 0, // Loop index
Ids = [],
msgObject = {
"SenderID":$scope.pageId,
"PageID" : $scope.pageId,
"Date":Date.now().toString(),
};
callAjax = function (value) {
var dfdAjax = $.Deferred();
$.ajax({
method: 'POST',
url: '///url',
async:true,
data: msgObject,
headers: {
'Content-Type': 'application/json'
}})
.then(function(response) {
dfdAjax.resolve(response);
})
.catch(function(response){
dfdAjax.reject(response);
});
return dfdAjax.promise();
},
requestAjax = function (value) {
return callAjax(value);
};
dfd.resolve();
for (x = 1; x <= Ids.length; x++) {
dfdNext = dfdNext.pipe(function () {
return requestAjax(value).
done(function(response) {
// Process the response here.
});
});
}
You could use $q.all() and simplify your syntax, you'll just need to inject the $q service.
The below code adds all the promises returned by $http into a single array, executes the promises using $q.all() and then gathers their results.
var requests = [];
for(var w = 0; w < Ids.length; w++) {
var req = $http({
method: 'POST',
url: '///url',
headers: { 'Content-Type': 'application/json' },
data: {
SenderID: $scope.pageId,
PageID: $scope.pageId,
Date: Date.now().toString(),
}
})
.catch(function(err) {
// handle err
});
requests.push(req);
}
$q.all(requests)
.then(function (results) {
// handle results
});;

Categories

Resources