Bug Tracker

Opened 11 years ago

Closed 11 years ago

Last modified 10 years ago

#11013 closed enhancement (fixed)

Deprecate use of Deferred/Promise with sync $.ajax

Reported by: jaubourg Owned by:
Priority: low Milestone: 1.8
Component: ajax Version: 1.7.1
Keywords: Cc:
Blocked by: Blocking: #10467

Description (last modified by Rick Waldron)

The async option will allow, if set to false, to make synchronous ajax requests.

It is the worst possible way to make ajax requests (it hangs the javascript VM), it also makes it impossible to implement Deferreds in a truly asynchronous fashion (because we cannot assume ajax is always asynchronous).

Sadly, this synchronous possibility is used internally in jQuery in order to execute script tags with src attributes in html fragments: https://github.com/jquery/jquery/blob/master/src/manipulation.js#L350

However this will not work for cross-domain, so I think we could very well "remove" this feature and document everything properly.

Change History (45)

comment:1 Changed 11 years ago by jaubourg

Component: unfiledajax
Description: modified (diff)
Keywords: 1.8-discuss added
Milestone: None1.8
Priority: undecidedlow

comment:2 Changed 11 years ago by jaubourg

Summary: Remove async options from $.ajaxRemove async option from $.ajax

comment:3 Changed 11 years ago by jaubourg

Blocking: 10467 added

comment:4 Changed 11 years ago by jaubourg

Description: modified (diff)

+1

comment:5 Changed 11 years ago by dmethvin

Description: modified (diff)

+1, Deprecate yes, remove no (at least not any time soon). People might think twice about using a deprecated feature which is our goal here.

comment:6 in reply to:  5 Changed 11 years ago by jaubourg

Replying to dmethvin:

+1, Deprecate yes, remove no (at least not any time soon). People might think twice about using a deprecated feature which is our goal here.

No, the goal would be to remove the option so that we are sure to only have asynchronous ajax requests. Deprecating sadly won't solve any problem.

comment:7 Changed 11 years ago by dmethvin

Status: newopen

comment:8 Changed 11 years ago by Timmy Willison

Description: modified (diff)

-1, I agree it's bad, but unfortunately used far too often

comment:9 Changed 11 years ago by Rick Waldron

Description: modified (diff)

-1

comment:10 Changed 11 years ago by dmethvin

Keywords: 1.8-discuss removed

This was voted neutral but in any case we cannot remove async without first deprecating it for a major version cycle. I'll leave this open.

comment:11 Changed 11 years ago by scottgonzalez

How many bytes are we gaining by adding an option for deferreds to allow sync resolution as I suggested in #10467? I can't imagine it would be too much. If deferreds defaults to forced async resolution, and sync XHR was the only time we set the flag, then the deprecation could go in and we'd have an easy upgrade path for removing sync XHR if we ever got to that point.

comment:12 Changed 11 years ago by JiDW

-1. Sometimes, a synchronous XHR request is exactly what you want to do (ie block the VM)

comment:13 Changed 11 years ago by anonymous

Synchronous ajax requests are also the *only* type allowed (currently) in Webkit browsers after a form submission has taken place.

It's extremely useful to be able to perform an ajax request after a form submission, whether to update some data on the screen, or to implement a progress/polling tracker for a large form upload that contains uploaded files.

If this option is removed I don't know if there is a suitable workaround...

https://bugs.webkit.org/show_bug.cgi?id=23933

comment:14 Changed 11 years ago by scottgonzalez

To complete anonymous' comment, sync XHR is also required for communication in beforeunload.

comment:15 Changed 11 years ago by dmethvin

Resolution: fixed
Status: openclosed
Summary: Remove async option from $.ajaxDeprecate/remove async option from $.ajax

Marked for deprecation in 1.8, a new ticket will be created when it is removed.

comment:16 Changed 11 years ago by akmurray@…

There is no technical reason to deprecate or remove this feature. Most people would agree that async=true is the way to go most of the time, but is isn't a functionally invalid option.

This feature is not similar to "browser sniffing the user agent" so don't treat it that way.

comment:17 Changed 11 years ago by ajm

It's also the best possible way to make synchronous requests, which is necessary from time to time as the commenters above point out.

This seems like you're removing utility from $.ajax - utility that's part of the XHR spec - and feels an awful lot like a regression.

Are we really going to need to duckpunch synchronous XHR into jQuery > 1.9?

comment:18 Changed 11 years ago by anonymous

Bad idea that asumes a lot about how other people are implementing ajax. I don't disagree that it's bad to load stuff synchronously, and I do believe that jQuery itself should stop using it INTERNALLY, but I don't advise limiting users of having such option.

Some legacy code that is written the old fashion way still works and transition may not be so straight forward, if you just remove it, it will cause too many problems to handle at once.

comment:19 Changed 11 years ago by dmethvin

We can still support async via a compat plugin if needed, but leaving it in blocks several other important features like consistently async resolution of Deferreds.

Legacy code can either choose to include the compat plugin or continue to use an older version of jQuery--including 1.8, which has only deprecated the behavior but still allows it.

comment:20 Changed 11 years ago by Gregory Jacobs <greg@…>

This is an awful, awful idea. As other users have mentioned, there is *no way* to guarantee that an ajax request completes in a beforeunload event handler without a synchronous request. And a backward compatibility plugin for this? Come on...

On our site, we use a beforeunload handler to persist any modified data to the server should the user navigate away from the page without saving their data. Yes, we have an auto-save, but it only runs after a timeout of inactivity (i.e. we don't want to run a save request for each character that the user types in...). The only way to get this request to execute when the user navigates away from the page is with a synchronous request.

So while it is true that in the general case, one doesn't want to use a synchronous request, we still need the ability to do so when needed (e.g. this situation). Therefore, to remove something like this from a generalized library is not only shortsighted, but terrible library design. If you want to discourage newbies from using synchronous requests, then discourage it in the docs. Don't simply remove a feature which is necessary.

And to include a backward compatibility plugin just for this feature? We use the CDN url to promote caching of the library between multiple websites. We don't want to include yet another request in the page for a backward compatibility plugin to provide a *necessary feature* which gives our users the best experience.

comment:21 Changed 11 years ago by dmethvin

For the beforeonload case it seems like this should do. If so, it doesn't seem like too much of a burden.

function savePreciousData(url, data){
  var xhr = new XMLHttpRequest();
  xhr.open("POST", url, false);                             
  xhr.send(JSON.stringify(data));
}                           

comment:22 in reply to:  21 Changed 11 years ago by Gregory Jacobs <gjslick@…>

Replying to dmethvin:

For the beforeonload case it seems like this should do. If so, it doesn't seem like too much of a burden.

function savePreciousData(url, data){
  var xhr = new XMLHttpRequest();
  xhr.open("POST", url, false);                             
  xhr.send(JSON.stringify(data));
}                           

So you're saying that we should request a change in the vendor library that we use (Backbone.js) which relies on jQuery for its ajax functionality, which currently accepts a config option of async: true/false, to instead build out the complete set of raw XMLHttpRequest code into the library just for the synchronous case?

Not to mention other libraries out there that support this?

...

comment:23 Changed 11 years ago by kristo@…

It is wrong to deprecate valid functionality of a browser. It's just insane how many occasions there are where this is useful, especially on requests where it is needed to pause the script while request happens.

Do not deprecate this. Period.

comment:24 Changed 11 years ago by dmethvin

Summary: Deprecate/remove async option from $.ajaxDeprecate async option from $.ajax

Clarifying the title to reflect the action taken in 1.8.

comment:25 Changed 11 years ago by anonymous

If this is deprecated & eventually removed, you oblige yourselves to provide documentation for resolving the problems it will create. For instance, form validation that occurs during the submit event through AJAX (the ubiquitous Validation plugin offers this) often creates a race condition that only a synchronous request can resolve (AJAX request against a database, return, valid/invalid, continue). Not to mention the other examples provided in previous comments.

As stated above, removing valid behavior & forcing dependent libraries to re-implement it from the ground up is a poor decision.

comment:26 Changed 11 years ago by anonymous

There are numerous valid use cases for synchronous Ajax. Discouraging it is good, but removing it is a bad idea.

An other not yet mentioned use case is when you need to do an a an Ajax request before a window.open (e.g. because the URL depends on data returned by the request) : doing the request asynchronously would cause the call to be blocked by the popup blocker since the stack trace does not originate from a user generated event anymore.

comment:27 Changed 11 years ago by anonymous

-1 - I am also in the boat as a lot of the voices on here, removing this option will have negative ramifications on my site. I am very disappointed in seeing this.

comment:28 Changed 11 years ago by anonymous

I agree with the others who have commented here. Asynchronous Ajax requests are the preferred method of communication, but there are times where we need to perform a synchronous request so that we can poll the server or save sensitive data before allowing the user to make any more changes to the page. Deprecate this if you must, as that may discourage those new to jQuery from using it all the time, but you cannot remove core functionality from your library like this. The change from .attr to .prop was already a pain to deal with, and you eventually backed down and supported backwards compatibility. It is very short-sighted to think that the backlash to this will not be just as large.

comment:29 Changed 11 years ago by dmethvin

Sorry about the lack of clarity. The ticket is saying that $.ajax({ ... async: false ... }) is being deprecated, and that option will eventually be removed.

It does not say there are no valid use cases for sync ajax. It does not say you are all bad people for doing sync ajax.It does not say there will be no way to do sync ajax--only that it won't be via $.ajax.

As the ticket explains, the problem is that async: true is so different that it prevents the rest of the $.ajax plumbing and dependencies from being fixed. For example, we cannot consistently call Promise callbacks asynchronously if $.ajax supports Promises with async: false.

We *would* like to collect some real-world uses of sync ajax. Please post some links to your code, so we can understand the use cases we need to address. My working assumption is that sync ajax requests are very simple and don't require things like prefilters or non-XHR transports (duh!).

For example, there is one case in the Backbone.js code, and I've already talked with tbranyen about it. But the usage there is trivial and would be easy to fix in several ways since it isn't using much of $.ajax at all. Imagine a hypothetical $.sjax({ url: "/", success: function(){ ... }) instead for example.

comment:30 Changed 11 years ago by Joe T.

Here's my real-world workflow where only a sync Ajax worked for me. Not saying it can't be rearranged to allow an async. (For the record, i did read the original ticket closely enough to know you're not wagging a finger at sync Ajax calls...) ;)

My application lets a user enter a customer name, and when they select the customer, info is pulled in from a 3rd party database, populating a form. The purpose is this data is submitted & pushed to a different system, but allows my user to edit data if necessary & add a couple required values the original data doesn't track.

The user isn't allowed to edit certain values (financial info), so if the source is invalid, they have to go back & fix it there. To deter edit attempts, the inputs are set to read-only. Once the user has filled in the additional info, they submit. The form is validated using Validation, with one field that MUST be verified against a different database. If it's invalid, the submit should fail & force the user to fix the value in the original source.

Since the value is validating in the submit event via Ajax, i can't use async because the submit action will proceed before a response is received. My sync request checks the value and sets an event-level (set within the event handler, but outside the Ajax call) boolean. That and the rest of the form validation determine whether the submit event continues.

As i've typed this up, i can think of a couple other places where i will try to fix this race condition, but it's an example of where sync requests would be legit & necessary barring alternate solutions.

comment:31 Changed 11 years ago by dmethvin

Let me make clear what we are asking for. We do not need scenarios. We need the actual call to $.ajax you make. When possible provide links to code on Github, as with this link.

comment:33 Changed 11 years ago by James Greene <james.m.greene@…>

A huge -1.

I enjoy Promises, though I tend to prefer the q implementation much more than jQuery's. However, I am shocked that producing Promises is taking priority over providing a cross-browser implementation of synchronous "AJAX" ("SJAX") requests, which is a very core concept to the web and sometimes a necessity -- I know we have one or two SJAX calls in our very expensive proprietary product today that cannot be removed. While Dave gives a 3-line alternative, I am disappointed as you jQuery devs more than most others know that the provided code is NOT correct for oldIE.

I would much prefer this be split into a separate "sjax" method [as Dave alluded to] in jQuery core (well, ajax) that DOES address cross-browser compatibility (at least through jQuery 1.9) than to force jQuery consumers to roll their own cross-browser "sjax" jQuery plugin or have to add another script request to pull such a plugin in.

One of the main uses in our product of an SJAX call is when a tiny Flash object is used to inject into the user's clipboard. Because this is only allowed by Flash security when a user interaction triggers it (e.g. mouse click on Flash object), the call to the server to dynamically fetch data that it invokes MUST return synchronously to still be within the requirements of the Flash security context necessary to allow clipboard injection.

comment:34 Changed 11 years ago by James Greene <james.m.greene@…>

Sorry, to clarify the workflow there:

  1. The Flash object invokes a JS function via ExternalInterface (not dissimilar from an SJAX interaction itself)
  2. The JS function does some work/calculation, makes an SJAX call to the server
  3. The server does some additional work, returns JSON response to the JS function
  4. JS function returns the JSON back to the Flash caller
  5. Flash parses the JSON object and injects it into the various sectors of the user's clipboard (plain text, RTF, HTML).

comment:35 Changed 11 years ago by anonymous

I'd also like to say that we use synchronous Ajax requests in development to resources that are packaged in production (the most common case are external view files). Using synchronous requests prevents users from having to make callback functions constantly.

comment:36 Changed 11 years ago by halcyon1234

-1. I use async:false for on-the-fly form validation. Specifically, the user enters their postal code. I need to do a complex db-look up to see if that is a supported postal code before they fill out any more of the form. There is a .Net CustomValidator, which calls a server-side PageMethod with

$.ajax{async:false, onSuccess:function(r){args.IsValid = r.IsValid}}

A .Net CustomValidator expects args.IsValid to be set before it exits its validation function. If I did async:true, the function would exit before onSuccess, and the validator would not work.

comment:37 Changed 11 years ago by gfbj@…

One instance of $.ajax async false: I have an application that depends on blur() to $.ajax to check whether a user name is available. Doesn't make sense to tell the user anything until the server replies.

I have other instances of async=false in other applications of projects I am no longer assigned to. If you want, I could go find them.

comment:38 Changed 11 years ago by dmethvin

@gfbj, that would be good. We would like to see the actual code, not a description of the application of the code.

comment:39 Changed 11 years ago by scottgonzalez

@gfbj You should really use async in that case anyway.

comment:40 Changed 11 years ago by dmethvin

Summary: Deprecate async option from $.ajaxDeprecate use of Deferred/Promise with sync $.ajax

NOTE THE CHANGED SUBJECT

The use of the Deferred/Promise functionality in synchronous ajax requests has been deprecated in 1.8. The $.ajax method with async: false is supported but you must use a callback parameter rather than a Promise method such as .then or .done.

So, for example this is supported for sync ajax:

$.ajax({
   ... async: false ...
   success: fn1,
   error: fn2
});

This is deprecated for sync ajax:

$.ajax({
   ... async: false ...
}).then(fn1, fn2);

comment:43 Changed 11 years ago by anonymous

How would you go about opening a popup on the condition of a server check? For instance, if the server needs to connect to a 3rd party account using OAUTH but the access tokens are not yet in the system, or are no longer valid? The flow would be as follows: Click an element in the page -> request the server to interact with the 3rd party -> tokens are missing or invalid -> server responds with authentication url -> browser opens the url in a popup to avoid interrupting the regular work flow, the user authenticates in the popup... later steps omitted. As far as I know this flow is not possible with an asynchronous call to the server, since most browsers will catch the popup in the popup blocker. However, if the call is synchronous, the whole flow is within the click event handling and the popup WILL be allowed...

How would one go about implementing a flow like this with an asynchronous server call?

comment:44 Changed 11 years ago by dmethvin

You implement it with sync ajax. Did you read the ticket? Sync ajax isn't going away in 1.9.

Further off-topic comments will just be removed to prevent other off-topic posts from following.

comment:45 Changed 11 years ago by anonymous

Thanks, @dmethvin. This approach is a win-win, I think!

comment:46 Changed 10 years ago by anonymous

I would recommend updating the documentation to clarify what is/is not deprecated or more importantly, showing a clear example of how to do an async request, as previously you could get responseText simply $.ajax().responseText which does not require any of the xhr callbacks. There are alot of no longer working async:false examples out there in the upper ranks of google, and it'd save people alot of time if the defacto documentation had a specific async:false example. I came across a situation where I absolutely needed the ajax call to block until completed, had a great deal of difficulty getting it to work(as many examples online use $.ajax()responseText), and along the way was wondering if I was mireading the note in the documentation. Had me wondering if perhaps async:false itself was deprecated and perhaps it was just poorly worded documentation. Using the success:function(){} callbacks for a synchronous method seems counter intuitive and I would have never guessed that would be the solution. For those who haven't been intemately familiar with jquery through it's api evolution, that message about xhr callbacks versus "success/error/complete callback" is kind of mysterious, and I didn't really understand it until I finally found a working example and am looking at it again.

Would strongly recommend adding an example to this page(add whatever "Will cause browser to freeze/is generally bad practice" disclaimer you want): http://api.jquery.com/jQuery.ajax/

Maybe anchor link to the example, from the current note about deprecated async:false/xhr callbacks.

comment:47 Changed 10 years ago by anonymous

Sorry should have read "to do an async:false request"

comment:48 Changed 10 years ago by jaubourg

I'm surprised, $.ajax( { async: false } ).responseText should work, if not, this is a bug.

Note: See TracTickets for help on using tickets.