This question already has answers here:
XHR request is denoted as being cancelled although it seems to be successful [closed]
(2 answers)
Closed 9 years ago.
I'm getting a "canceled" status whenever I do a jquery $.post(). It seems like an asynchronous problem? I access the local project with http://127.0.0.1:8933/myproject/default/index.html
index.html:
<script>
$(document).ready(function() {
$('#mybutton').click(function() {
var post_url = get_url(); // resolves to "/myproject/default/process_func"
var data = ...;
do_action(post_url, data);
});
});
</script>
Click me!
util.js:
function doPost(url, data) {
$.post(url, data).then(doSuccess, doFail):
function doSuccess(data) {
alert('Successful!');
}
function doFail(data) {
alert('Failed!');
}
}
function do_action(url, data) {
var jsondata = JSON.stringify(data);
// some other stuffs...
doPost(url, jsondata);
}
The idea is to post some data back to the server for processing using json data. A user will click on the anchor button, the do_action() will fire, and then it'll be posted in doPost().
This seems to work since my I'm seeing the json data passed to my processing function on the server. However, I'd see the alert message "Failed!" pop up every time even though the data has been processed. In chromium's debug panel, I see under "Network" tab that the post has a status of "(canceled)" and the entire line would be highlighted in red. The Type stated is "Pending".
I think this is an asynchronous problem, but I'm unsure how to fix it.
Use this, to cancel the default navigatin behavior of the <a>:
$('#mybutton').click(function(e) { // <-- Add `e` here
e.preventDefault(); // <-- Add this line
var post_url = get_url();
var data = ...;
do_action(post_url, data);
});
I believe the AJAX request is being aborted by the browser because when clicking the link, although the request does get sent off, the link tries to navigate to a new page so the browser must abort open AJAX requests.
You can try this...
$('#mybutton').live("click", function(e) {
e.preventDefault();
var post_url = get_url();
var data = ...;
do_action(post_url, data);
});
Depending on the version of jQuery you are using, you can use jQuery.live() or jQuery.on() method
When the e.preventDefault() method is called if this method is called, the default action of the event will not be triggered.
Resources:
event.preventDefault()
$.live()
Related
I'm using a JS library to stream server-sent-events on my html page:
<html>
<textarea rows='14' id="value" placeholder="anything you want here"></textarea>
<button type="button" onclick="post(clip)">get</button>
</html>
<script type="text/javascript" src="sse.js"></script>
<script>
url = "http://ac6ba97b046a5dcc677e.elb.us-east-1.amazonaws.com/myapi";
let textArea = document.getElementById("value");
function clip(){
s = textArea.value;
s = s.slice(0, -5);
textArea.value = s;
console.log('hello');
}
function post(callback){
var v = String(textArea.value);
console.log(v);
var source = new SSE(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' },
payload: v
});
var arr = [];
source.addEventListener('message', function (e) {
arr.push(e.data);
textArea.value = arr.map(el => el || " ").join('');
});
source.stream();
callback();
}
</script>
When the button is clicked, data is sent to a server using POST method and the textbox is populated with data received from the server. I would like to clip the text in the textbox with clip() after the post() function is executed. Execution process must be like this:
1. post() logs textArea value
2. source.stream() is executed, textbox populated
3. clip() clips last 5 characters and logs 'hello'
But I instead get this:
1. post() logs textArea value
2. clip() clips last 5 characters and logs 'hello'
3. source.stream() is executed, textbox populated
For some reason clip() is being executed before source.stream() even after adding a callback.
The sse.js file that I'm using.
[EDIT] After moving callback() to the end of 'message' handler, the issue still persists:
function post(callback){
var v = String(textArea.value);
console.log(v);
var source = new SSE(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain' },
payload: v
});
var arr = [];
source.addEventListener('message', function (e) {
arr.push(e.data);
textArea.value = arr.map(el => el || " ").join('');
callback();
});
source.stream();
}
Does anyone know what might be causing this?
When your script calls source.stream();, it is doing an XMLHttpRequest.send() operation, which is async by default.
So, what is happening:
user clicks, and post() is called
SSE object and its event listener is set up
source.stream() is called, which sends the request to the server.
callback() (i.e. clip()) is called
Server sends back a response
Your message event handler is called
textArea.value is set
Luckily the fix is simple: you only want callback() to be called when a message is received. So move callback() to be at the end of the message event handler, not at the end of post().
It will do this after every message event that is received. If you only wanted it to happen after the first event, you will need to implement some logic to keep track of how many events have been received. (And if there will only be one message event, you should be using an Ajax call, not an SSE/EventSource call.)
UPDATE: Discussion in the comments are starting to get out of scope of your original question (where the answer is, simply put, "it is async, not sync"). But I think it is worth pointing out here that you set up a new SSE object every time the user clicks the button. Each SSE object will have its own dedicated TCP/IP socket and its own listener function.
This is (generally) not a good idea: instead create the SSE connection once at the start of your web app.
And, though your SSE polyfill library allows using POST, the standard SSE does not. If you only want the app to poll the server when the user presses the button, consider switching from using SSE to normal AJAX.
I want to send large files and report the upload status. It should be easy, and there are lots of answers on this site demonstrating it. But for some reason it doesn't work for me, not on Firefox, nor Chrome.
I've got a form with files (defined as <form id="form" method="post" enctype="multipart/form-data">) that I'm posting using this code:
function post() {
const request = new XMLHttpRequest();
request.upload.addEventListener("progress", (e) => {
console.log("progress: " + e.loaded + "/" + e.total);
});
request.open("post", formElement.action);
request.responseType = "json";
console.log("Start sending");
request.send(new FormData(document.getElementById('form');));
}
But the minute I click the button which activates this function, the browser stops responding until the whole operation is done and then it just renders the json response.
The console doesn't show any message (not even the "start sending", let alone the progress), and when I inspect the page after sending the post request, it shows as if it doesn't have any JS or DOM at all. i.e. the page is showing, but is not inspectable and non-interactive.
What am I doing wrong?
You need to prevent the default action of the form, as MaksTech pointed out. Just forget the jQuery part of his, only the following line is important:
e.preventDefault();
There are plenty of other solutions you can also modify the action of your form to the following
<form onsubmit="event.preventDefault(); post();">
or attach that listener accordingly
Your form submits as a normal form, so you should cancel that like so:
let $form = $('#form');
$form.on('submit', function (event) {
event.preventDefault();
// This will stop default submition so you can do it manually,
// i.e. as AJAX (XMLHttpRequest)
// Your function goes here...
});
Here's some more information about what's happening here on official JQuery docs.
I'm trying to prevent defaults on a click, call a page with ajax and trigger the click on complete, using this answer.
<a id="mylink" href="file.csv" download >Dowload</a>
<script>
var flag = false;
$('#mylink').on('click',function(e) {
// Result is the same with :
//$(document).on("click","#mylink",function(e){
if (flag === true) {
flag = false;
return;
}
e.preventDefault();
$.ajax({
url: "index.php?controller=admin&action=refreshFile",
complete: function() {
console.log('control'); // This is called
flag = true;
$('#mylink').trigger('click'); // This is not called
}
});
});
</script>
The call works but the link is not triggered after. The result is the same when the ajax call is set inside a separate function.
use window.location to call the link href
$('#mylink').on('click',function(e) {
e.preventDefault();
$.ajax({
url: "index.php?controller=admin&action=refreshFile",
complete: function() {
console.log('control'); // This is called
window.location = $('#mylink').attr("href");
}
});
});
or with one event listeners
var eventListener = function(e) {
e.preventDefault();
$.ajax({
url: "index.php?controller=admin&action=refreshFile",
complete: function() {
console.log('control'); // This is called
$('#mylink')[0].click();
$('#mylink').one('click', eventListener);
}
});
};
$('#mylink').one('click', eventListener);
I'm not sure what your flag is supposed to do. In your example it would mean the link only works every 2nd click.
P.s. Using the complete callback means it also works even when the ajax fails. You might want to change it to success.
Update
#Racil Hilan has a point: this solution is a little overkill when you could just call the link directly and return the correct file after the refreshFile action has been called.
TRy
var flag = false;
$('#mylink').on('click',function(e) {
// Result is the same with :
//$(document).on("click","#mylink",function(e){
if (flag === true) {
flag = false;
windows.location="file.csv";
}
e.preventDefault();
$.ajax({
url: "index.php?controller=admin&action=fileDownload",
complete: function() {
console.log('control'); // This is called
flag = true;
$('#mylink').trigger('click'); // This is not called
}
});
});
In my humble opinion, this is not the right design. Your Ajax is calling the index.php on the server before triggering the download. If the index.php is doing some security or critical stuff that MUST be done before allowing the user to download the file, then this design is absolutely insecure. You don't even need to be a hacker, simply copy the link file.csv and paste it in your browser's address bar, and you'll get the file without the Ajax.
You need to place the file.csv file outside your website folder (or maybe it is generated on the fly by the server code, so that' good too) and then the PHP page must run all the checks and if all run OK, it reads the file (or generate it) and returns the download to the browser (or an error message if the checks failed). This is how to secure file downloads on the server.
After doing all of that, it is a matter of preference whether you call the PHP directly from your link, or the link calls the Ajax function which in turn calls the PHP page and parse the download (this is a bit more complex, but doable). The only difference between the two methods is whether you want the page refreshed when the download (or error message) come back from the server.
If you want to take this advice, rephrase your question and select which way you want to go (i.e. direct link, or through Ajax), so we can help you.
this code works in Chrome & Firefox but not in IE9 ... need some hints ...
var obj = {
data: [],
json: function() {
var self = this;
$.getJSON("highscore.json", function(resp) {
self.data = resp.splice(0);
});
}
};
Update:
thx for your help ...
it was an issue from the ie9,
ie has thrown the error code "c00ce56e" - it's an issue with charset.
i'll try another header in the php scripts ...
thx # all
Your code looks fine to me, other than that data won't be populated until the json request is done, which is NOT instant because ajax is asynchronous.
obj.json();
alert(obj.data); // []
setTimeout(function(){
alert(obj.data); // ["foo","bar","foobar"]
},5000);
Update
I suggest adding a property to your object called request, and store the $.getJSON request in it. At that point it doesn't make sense to store the data directly on the object because you can always get it from the request.
var obj = {
request: {done:$.noop,fail:$.noop,always:$.noop},
json: function() {
this.request = $.getJSON("highscore.json");
}
};
obj.json();
// you can run the following as many times as you need to use the data.
obj.request.done(function(data){
alert(data.splice(0));
});
just note that in it's current form you must call .json() before you can add callbacks to the request.
Problem I am making ajax call to server1 i.e. csce and once I got the response I am sending the response as contents to server2 i.e.yahoo server after getting response from there I want to refresh the page or atleast redirect it to the same page. Both ajax calls are working fine. The contents I am sending are also saved the only problem is that I have to manually refresh the page to see the changes. I want to refresh the page once the contents are saved on yahoo. I tried reload and redirect commands in success function of yahoo. But nothing works. I can see the both ajax calls in the HTTPfox but not the redirect.
The url from which i am making calls is different from the url where contents are saved thats why I need to refresh the page to see the changes. i.e. I am saving in yahoo/save while sending contents and seeing changes at yahoo/edit.
I am not sure where I am going wrong. Here is my code I am using. Can anyone suggest where I am going wrong. If my problem is not clear kindly do ask me to clarify more. Thanks.
This code is the code:
function handleButtonClick()
{
// Declare the variables we'll be using
var xmlHttp, handleRequestStateChange;
// Define the function to be called when our AJAX request's state changes:
handleRequestStateChange = function()
{
// Check to see if this state change was "request complete", and
// there was no server error (404 Not Found, 500 Server Error, etc)
if (xmlHttp.readyState==4 && xmlHttp.status==200)
{
var substring=xmlHttp.responseText;
alert(substring);// I am able to see the text which is returned by the server1 i.e csce
var handleSuccess = function(o)
{
if(o.responseText !== undefined)
{
console.log(o.responseText);
**window.location.reload()** // also I tried to redirect it to the same site but that also not works
}
};
var callback ={ success:handleSuccess, failure: function(x) {
console.error(x) }, argument: ['foo','bar']};
var request = YAHOO.util.Connect.asyncRequest('POST','http://yahoo.com******', callback, substring);
}
}
xmlHttp = new XMLHttpRequest();
xmlHttp.open("GET", "http://cse*****id=c6c684d9cc99476a7e7e853d77540ceb", true);
xmlHttp.onreadystatechange = handleRequestStateChange;
xmlHttp.send(null);
}
Do you just want to display the content in your page? Why don't you try something along the lines of document.getElementById('divID').innerHTML = xmlHttp.responseText;?
With divID being the id of a div that you want to fill the content with.
try following in the handleRequestStateChange function
window.location.href = window.location.href;