I have noticed that I'm having problems when I'm using AJAX in jQuery inside a .each() loop. Only the first record in my database are being updated when the script executes.
Here's my script:
function save(){
var _userTypeId;
var _userTypeName;
var _isDeleted;
var request;
$("tr.recUserType").each(function(){
$this = $(this);
_userTypeId = $this.find("#userTypeId").html();
_userTypeName = $this.find("#userTypeName").val();
_isDeleted = $this.find("#isDeleted").val();
request = $.ajax({
url: "save.php",
type: "POST",
data: {userTypeId: _userTypeId, userTypeName: _userTypeName, isDeleted: _isDeleted}
});
});
request.done(function(){
document.location.reload();
});
request.fail(function(){
alert("Request Failed!");
});
}
And the contents of save.php:
<?php
include_once "globals.php";
dbConnect();
$isExisting = mysql_query("SELECT COUNT(userTypeId) AS userCount FROM userType WHERE userTypeId='".$_POST['userTypeId']."';");
$result = mysql_fetch_array($isExisting);
//original: if(!$result['userCount'] = 0) <-- This was a logical error
if($result['userCount'] != 0)
mysql_query("UPDATE userType SET userTypeName='".$_POST['userTypeName']."', isDeleted='".$_POST['isDeleted']."' WHERE userTypeId='".$_POST['userTypeId']."';");
else
mysql_query("INSERT INTO userType VALUES('', '".$_POST['userTypeName']."', '".$_POST['isDeleted']."');");
echo mysql_error();
dbClose();
?>
I have read that I have the option to use synchronous instead of asynchronous, but I have also read it is not a good practice.
So how do I actually get this done asynchronously and fix the problem?
There are a couple things happening here.
By re-using the
request
variable in the loop (scoped outside the loop), every iteration of the loop assigns a new object to request, overwriting what was there. This means you are only setting the response handlers for the last iteration.Your
request.done
method reloads the page, which in practice will halt the other requests and...You are looking up the same
userTypeId
for each iteration of the loop, as mentioned by @SeanjQuery's $.ajax() returns a jQuery XMLHttpRequest Object (a "jqXHR"). You are storing this object into your "response" variable.
Your problem is scope. You are storing all N of your jqXHRs into the same "request" variable. By the end of your loop, "request" is only pointing to the last jqXHR, and thus .done() will only be called when your LAST request completes.
As Karl Anderson pointed out, you should store ALL of your jqXHRs into an array, and then execute a single callback once ALL of them have [asynchronously] completed.
Also, avoid the delicious temptation to use $.ajax()'s "async: false". The browser will be forced to hang until a request completes, which is bad. You can pretty much always accomplish a $.ajax() call asynchronously; it may require some craftiness, but will definitely be for the better.
Your issue is in this block of code:
Since the actions are asynchronous, this line is being executed before the save is completing, thus after the first save does complete, it executes the
done
logic.You need to create an array of deferred objects that you can then wait until they are all executed before proceeding forward with your logic using the jQuery
.when()/.then()
functions.Here is a previous StackOverflow question that details how to setup this situation. Please read the accepted answer of jQuery when each is completede trigger function.