Javascript/jquery iterate async problems - javascript

I would like to iterate through a certain amount of pages, and populate them with content using ajax calls. The problem is, when I put the ajax calls inside the iteration function it has problems with the synchronous nature of javascript. The iteration has already continued before the ajax call is completed. So I made a workaround where I made the ajax call in a setTimeout, which works fine. But I don't really like this method, and was wondering if there is an alternative (better) solution. (I know that jQuery provides a async: true option, however that did not work)
function populatePages(i) {
pageId = PageIds[i];
containerId = pageIdContainer[i];
$j.ajax({
type: 'GET',
dataType: 'html',
url: url,
data: { pageid: pageId, containerid: containerId },
success: function(data) {
//populate the DIV
}
});
}
i = 0;
x = 50;
$j.each(pagesIds, function(){
setTimeout("populatePages("+i+")", x);
x = x + 50;
i++;
});

Try this (not tested)
function populatePages(i) {
console.log('populatePages', i)
pageId = PageIds[i];
return $.ajax({
type: 'GET',
dataType: 'html',
url: '/echo/html',
data: { pageid: pageId},
success: function(data) {
}
});
}
function messy(index){
console.log('messy', index)
if(index >= PageIds.length){
return;
}
populatePages(index).always(function(){
console.log('complete', index)
setTimeout(function(){
messy(index + 1)
});//to prevent possible stackoverflow
})
}
PoC: Fiddle

Related

How to show progress indicator while looping ajax .forEach() jquery

i want to display progress indicator while ajax request looping using .forEach()
example
2 of 12 success
html code
<h2 class="progress_text"></h2>
<a onclick="getdata()">Get Data</a>
jquery code
var arr = ['1','2','3','4','5','7','8','9','10','11','12']
var sum = arr.length
var progress = 0
function getdata(){
arr.forEach(start_getdata)
}
function start_getdata(value, index, array) {
$.ajax({
type: 'GET',
url: "/get_data",
async: false,
dataType: 'json',
data: {
id: value,
},
success: function(data) {
$(".progress_text").html(""+progress ++ +" of "+ sum +" success")
},
error: function(x, e) {
console.log("Gagal")
}
});
}
my code only show once when .forEach() done execute
please answer if someone has same problem
thank you

Abort all remaining AJAX requests

I am running an AJAX request when the user types in an input field and then displaying the result on the page. When the user presses the backspace to delete all of what they've inputted, I use .empty to remove the result from the page.
However, if you press the backspaces really quickly, the result is removed from the page, but then because the last AJAX query hasn't last executed, the result from that query appears!!!
I have looked at Abort Ajax requests using jQuery but that didn't help, and have tried adding return: false; after $("#results").empty(); to no avail.
If there are any remaining AJAX calls when if(this.value.length < 1) { is true, I would like to abort them all inside that function.
$("input#enter").keyup(function() {
if(this.value.length < 1) {
$("#display").empty();
}else{
$.ajax({
type: "POST",
url: "getdata.php",
data: "title=" + this.value,
success: function(data) {
$("#display").empty();
$("#display").html(data);
}
});
}
});
You can use $.active to check if $.ajax() call is active before calling next $.ajax()
$("input#enter").keyup(function() {
if(this.value.length < 1) {
$("#display").empty();
}else{
if (!$.active) {
$.ajax({
type: "POST",
url: "getdata.php",
data: "title=" + this.value,
success: function(data) {
$("#display").empty();
$("#display").html(data);
}
});
}
}
});
You can also include attaching .ajaxComplete() to document to call next $.ajax() call when current call completes
function request(value) {
return $.ajax({
type: "POST",
url: "getdata.php",
data: "title=" + value,
success: function(data) {
$("#display").empty();
$("#display").html(data);
}
});
}
$("input#enter").keyup(function() {
if(this.value.length < 1) {
$("#display").empty();
}else{
if (!$.active) {
request(this.value)
} else {
$(document).one("ajaxComplete", function() {
request(this.value)
})
}
}
});
One approach to abort requests is to use XMLHttpRequest(), push requests to an array, then call .abort() on each element of the array
function request(data) {
let fd = new FormData();
fd.append("html", data);
fd.append("delay", Math.floor(Math.random() * 10));
let xhr = new XMLHttpRequest();
xhr.open("POST", "/echo/html/", true);
xhr.onload = function() {
console.log(xhr.responseText);
}
xhr.onabort = function() {
console.log("request " + requests.indexOf(xhr) + " aborted")
}
xhr.send(fd);
return xhr
}
function abortAllRequests() {
requests.forEach(function(xhr, index) {
xhr.abort()
})
}
var requests = [];
requests.push(request(123), request(456));
abortAllRequests();
jsfiddle https://jsfiddle.net/onguym5y/
You talk about aborting ajax requests. It would be sufficient to wait until the request returns and then simply do nothing. Yes, if you were doing a lot of large requests it might improve performance if you cancelled them. But that means using jqXhr objects, and personally I prefer to stick to jQuery where possible.
You could have a variable telling you how up-to-date the #display is. It would store the time of sending of the last ajax request that was used to update it. If you get a response from an earlier request, ignore it.
var lastUpdateTime = 0;
$("input#enter").keyup(function() {
var now = new Date().getTime();
if(this.value.length < 1) {
$("#display").empty();
lastUpdateTime = now;
}else{
$.ajax({
type: "POST",
url: "getdata.php",
data: "title=" + this.value,
success: function(data) {
if (now < lastUpdateTime) {
return;
}
$("#display").empty();
$("#display").html(data);
lastUpdateTime = now;
}
});
}
});

JavaScript/jQuery callback using Ajax

I'm having trouble with my functions running before Ajax requests (the first to a local JSON, the second to an online resource) have finished.
In this example I want countTheMovies to run at the end after my application has got all the information it needs and populated the divs. Instead it's running straight away.
I tried to delay it using an if condition, but with no joy. I've also tried with callbacks, but think I must be getting those wrong (I'm assuming callbacks are the answer). I'm aware of timed delays, but because in the actual project I'm sourcing 250+ movies (and because a timed delay seems like cheating) I thought I'd ask here instead.
Can anyone recommend JavaScript or jQuery code to fix this problem?
$(function(){
getMovieList();
});
function getMovieList() {
$.ajax({
url: "movielist.json",
type: "GET",
dataType: "JSON",
success: function(data) {
for (var i = 0; i < data.length; i++) {
var title = data[i].title.toLowerCase().split(" ").join("+");
var year = data[i].year;
i === data.length - 1
? getMovieInfo(title, year, true)
: getMovieInfo(title, year, false);
}
}
});
}
function getMovieInfo(title, year, isLast) {
$.ajax({
url: "https://www.omdbapi.com/?t=" + title + "&y=" + year + "&plot=short&r=json",
type: "GET",
crossDomain: true,
dataType: "JSON",
success: function(val) {
if (!val.Error) {
movie = title.replace(/[^a-z0-9\s]/gi, '');
$("#app").append(
// appending info to divs
);
}
}
});
if (isLast) countTheMovies();
};
function countTheMovies() {
$("#app").append("There are " + $(".movie").length + " movies.");
}
A plunker of my failings: https://plnkr.co/edit/0mhAUtEsaOUWhkZMJqma?p=preview
You've almost got it!
The same way that you call getMovieInfo in the success callback of getMovieList, you should be calling countTheMovies in the success callback of getMovieInfo.
As Jacob said above, move the countTheMovies call inside the AJAX request.
$(function(){
getMovieList();
});
function getMovieList() {
$.ajax({
url: "movielist.json",
type: "GET",
dataType: "JSON",
success: function(data) {
for (var i = 0; i < data.length; i++) {
var title = data[i].title.toLowerCase().split(" ").join("+");
var year = data[i].year;
i === data.length - 1
? getMovieInfo(title, year, true)
: getMovieInfo(title, year, false);
}
}
});
}
function getMovieInfo(title, year, isLast) {
$.ajax({
url: "https://www.omdbapi.com/?t=" + title + "&y=" + year + "&plot=short&r=json",
type: "GET",
crossDomain: true,
dataType: "JSON",
success: function(val) {
if (!val.Error) {
movie = title.replace(/[^a-z0-9\s]/gi, '');
$("#app").append(
// appending info to divs
);
if (isLast) countTheMovies();
}
}
});
};
function countTheMovies() {
$("#app").append("There are " + $(".movie").length + " movies.");
}
Just put your countTheMovies() logic inside of the success callback of the AJAX request in getMovieInfo if you want it to run on success.
You can call your countTheMovies() function from inside the success field of your Ajax call. This way it will make the function call when you intend it to.
Try out this
$(function(){
getMovieList();
});
function getMovieList() {
$.when( $.ajax({
url: "movielist.json",
type: "GET",
dataType: "JSON",
success: function(data) {
for (var i = 0; i < data.length; i++) {
var title = data[i].title.toLowerCase().split(" ").join("+");
var year = data[i].year;
i === data.length - 1
? getMovieInfo(title, year, true)
: getMovieInfo(title, year, false);
}
}
}) ).then(function( data, textStatus, jqXHR ) {
countTheMovies();
});
}
function getMovieInfo(title, year, isLast) {
$.ajax({
url: "https://www.omdbapi.com/?t=" + title + "&y=" + year + "&plot=short&r=json",
type: "GET",
crossDomain: true,
dataType: "JSON",
success: function(val) {
if (!val.Error) {
movie = title.replace(/[^a-z0-9\s]/gi, '');
$("#app").append(
// appending info to divs
);
}
}
});
};
function countTheMovies() {
$("#app").append("There are " + $(".movie").length + " movies.");
}

How to check json response taken longer than 5 seconds?

Below is the sample code of my function. in the for loop one by one product id is pass in the ajax function and get product price from the php file as response and write it and html.
for(var i=0; i < data.products.length; i++){
var doc = data.products[i];
$.ajax({ // ajax call starts
url: 'product.php',
data: { product_id: doc.id },
dataType: 'json',
success: function(data)
{
document.getElementById('price_price'+data.product_id+'').innerHTML = data.products_price;
}
});
}
I have found that sometimes it takes a more time for price to display. i want to check which record is taking time to load. how can check to detect when it takes longer than 5 seconds for the price to load?
Something like this....
var ajaxTime= new Date().getTime();
$.ajax({
type: "POST",
url: "some.php",
}).done(function () {
var totalTime = new Date().getTime()-ajaxTime;
// Here I want to get the how long it took to load some.php and use it further
});
Also, by the way, if you want to prevent sending (i+1) request, before (i) is completed, you'd maybe want to use syncronous ajax request instead of async.
Try to log timestamp beforesend and success or error
$.ajax({ // ajax call starts
url: 'product.php',
data: { product_id: doc.id },
dataType: 'json',
beforeSend: function() {
console.log(new Date().getSeconds());
}
success: function(data)
{
console.log(new Date().getSeconds());
document.getElementById('price_price'+data.product_id+'').innerHTML = data.products_price;
}
});
Use setTimeout, like this:
var timeoutTimer = setTimeout(function() {
// time out!!!.
}, 5000);
$.ajax({ // ajax call starts
url : 'product.php',
data : {
product_id : doc.id
},
dataType : 'json',
success : function(data) {
document.getElementById('price_price' + data.product_id + '').innerHTML = data.products_price;
},
complete : function() {
//it's back
clearTimeout(timeoutTimer);
}
});

Pausing for loop after every execution

i have a page, wherein i am using a ajax for inserting records... now in javascript i am using a for each loop to loop the html table and insert the rows in database. but happens is as foreach loop executes fast, it sometime, does not insert some records.. so i want to make the loop sleep for sometime once it has executed first and thereafter...
is there any way to pause the for loop.. i used setTImeout.. but it just delay it first time and not consecutive times...
here's my code.
function AddTopStories() {
$("#tBodySecond tr").each(function (index) {
$.ajax({
type: "POST",
url: "AjaxMethods.aspx/AddTopStoriesPosition",
data: "{'articleID':'" + $("td:nth-child(1)", this).text() + "','siteID':1}",
dataType: "json",
contentType: "application/json",
success: function (data) {
window.setTimeout(showSuccessToast(data.d), 3000);
},
error: function (data) {
window.setTimeout(showSuccessToast("Error:" + data.reponseText), 3000);
}
});
});
}
Please help me to resolve this issue... its utmost important.
*************************************UPDATED CODE AS PER THE CHANGES BY jfriend00*********
function AddTopStories() {
var stories = $("#tBodySecond tr");
var storyIndex = 0;
function addNext() {
if (storyIndex > stories.length) return; // done, no more to get
var item = stories.get(storyIndex++);
alert($("td:nth-child(1)", item).text());
addNext();
}
}
This just does not do anything... does not alert...
I'd recommend you break it into a function that does one story and then you initiate the next story from the success handler of the first like this:
function AddTopStories() {
var stories = $("#tBodySecond tr");
var storyIndex = 0;
function addNext() {
if (storyIndex >= stories.length) return; // done, no more to get
var item = stories.get(storyIndex++);
$.ajax({
type: "POST",
url: "AjaxMethods.aspx/AddTopStoriesPosition",
data: "{'articleID':'" + $("td:nth-child(1)", item).text() + "','siteID':1}",
dataType: "json",
contentType: "application/json",
success: function (data) {
addNext(); // upon success, do the next story
showSuccessToast(data.d);
},
error: function (data) {
showSuccessToast("Error:" + data.reponseText);
}
});
}
addNext();
}
Ugly, but you can fake a javascript 'sleep' using one of the methods on this website:
http://www.devcheater.com/

Categories

Resources