问题
What causes ajax to send more than one POST request at the same time? It is hard to reproduce since it happens about 2% of the time. It seems to happen on bad/mobile networks. We are using Chrome on Android.
How Form Works
- .keyup event listener waits for N characters and then sends the data from the form after some minor validation via an ajax call.
- The form is immediately cleared so that another request can be sent.
- onSuccess returns and updates a totals table.
Problem The system saves multiple records when it should only save one.
What I've found/tried
- Only one call is returned to the UI. The onSuccess is only called once when this happens. The UI has a total that gets out of sync whenever this occurs.
- Double scan or double click? No, I've created a check to prevent duplicate data of the same value to be sent back to back.
- The server access logs show duplicate requests at the exact same time.
- I thought it was due to HTTP/1.1 RFC 8.2.4: which says POST requests can be retried, so I made change to controller to check for a timestamp (p) but since the request is at the exact same time, my code does not catch that the same p is coming through. https://stackoverflow.com/a/14345476/2537574
[22/Aug/2016:07:14:12 -0700] "POST /api/save?p=14718752538085 HTTP/1.1" 200 749
[22/Aug/2016:07:14:12 -0700] "POST /api/save?p=14718752538085 HTTP/1.1" 200 736
Event Binding Problem? I've made this change: https://stackoverflow.com/a/13475122/2537574 $('#field').unbind('keyup').keyup(function (e) {
Event Bubbling Problem? I've added this: https://stackoverflow.com/a/12708948/2537574 e.stopImmediatePropagation();
Attempt 6 - Did not work
unbind/bind is the old way of doing it. Now trying to use .off and .on and added e.preventDefault(). (https://stackoverflow.com/a/14469041/2537574)
$(document).off('keyup', "#field").on('keyup',"#field" ,function(e) {
e.preventDefault();
...
Attempt 7 - Did not work Added return false to end of keyup event listener.
$(document).off('keyup', "#field").on('keyup',"#field" ,function(e) {
e.preventDefault();
// checks for 6 chars and submits
return false;
)};
Attempt 8 - Debounce
$(document).off('keyup', "#field").on('keyup',"#field" ,debounce(function(e) {
// checks for 6 chars and submits
},50));
Attempt 9 - Updated web server Tomcat Updated to latest tomcat 7.70 but that did not fix the issue.
Current Code
$(document).off('keyup', "#field").on('keyup',"#field" ,debounce(function(e) {
e.preventDefault(); // attempt 6
if(e.which != 13){
if($(this).val().length ==6){
submitForm();
$(this).val('');
}
}
return false; // attempt 7
},50));
function submitForm(){
// Does some validation and if it passes calls:
persist();
}
function persist(){
var data = $("#Form").serialize();
var persistNum = Date.now()+''+(Math.floor(Math.random() * 9)); // used to prevent duplicate insertion on POST retry
$.ajax({
url: "/api/save?pNum="+persistNum,
context:document.body,
cache: false,
type: "POST",
data: data,
success:function(data, textStatus, jqXHR){},
error: function(jqXHR, textStatus, errorThrown)
{}
});
}
回答1:
I cant be certain this is your issue, but you definitely want to use a timer with keyup when using it to call ajax so that it only triggers when the user stops typing. Also, I would keep track of the submission state and only send if not already sending. This is what I use, and it may solve your problem:
var ajaxProcessing = false; // keep track of submission state
$('#field').unbind('keyup').keyup(function(e) {
if (ajaxProcessing) return; // if processing, ignore keyup
var $this = $(this);
keyupDelay(function() { // keyup timer, see below
if (e.which != 13) {
if ($this.val().trim().length == 6) { // good UX to trim the value here ;)
submitForm();
}
}
}, 500); // this sets it to go off .5 seconds after you stop typing. Change as needed
});
function submitForm() {
// Does some validation and if it passes calls:
persist();
}
function persist() {
ajaxProcessing = true; // set is proccessing
var data = $("#Form").serialize();
$.ajax({
url: "/api/save",
context: document.body,
cache: false,
type: "POST",
data: data,
success: function(data, textStatus, jqXHR) {
ajaxProcessing = false;
$("#Form").find("input[type=text], textarea").val(""); // reset form here
},
error: function(jqXHR, textStatus, errorThrown) {
ajaxProcessing = false;
alert('Form was submitted. It failed though because ajax wont work from SO');
}
});
}
// keyup timer funtion
var keyupDelay = (function() {
var timer = 0;
return function(callback, ms) {
clearTimeout(timer);
timer = setTimeout(callback, ms);
};
})();
input {
width: 400px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<form action="" id="Form">
<input type="text" maxlength="6" placeholder="type 6 chars here then stop" id="field">
</form>
来源:https://stackoverflow.com/questions/39092335/ajax-sends-multiple-posts-of-single-event