سؤال

I've seen a pattern of people converting their jqXHR objects (ie. those things JQuery returns when you do an AJAX operation) in to promises. For instance:

function doAJAX() {
    var jqXHR = $.get('http://www.example.com');
    return jqXHR.promise();
}

I've never bothered with this pattern because it doesn't seem to really offer anything. When people talk about converting $.Deffereds to $.Promises they recommend doing so to prevent the user from prematurely resolving the deferred. But jqXHRs already effectively are promises: they implement the promise interface and they can't be resolved prematurely.

But, I'm now working on a public-facing API, where I'm willing to bend over backwards (code-wise) if it will result in a better API for the customer. So, my question is, am I missing anything? Will throwing .promise() after every AJAX call that gets returned to the customer actually make things better for them in any way, or is this just an example of patternitis where people apply .promise() to their jqXHRs simply because they're used to doing it to their deferreds?

هل كانت مفيدة؟

المحلول

jQuery's jqXHR as returned from $.get() is already a fully functioning promise object, not a deferred object so it is already protected as a promise. You don't need to convert it into one and doing so only hides existing jqXHR functionality.

So, you can already directly do this:

 $.get('http://www.example.com').done(function() {
     // your code here
 });

Straight from the jQuery doc for $.get();

As of jQuery 1.5, all of jQuery's Ajax methods return a superset of the XMLHTTPRequest object. This jQuery XHR object, or "jqXHR," returned by $.get() implements the Promise interface, giving it all the properties, methods, and behavior of a Promise (see Deferred object for more information). ...

The Promise interface also allows jQuery's Ajax methods, including $.get(), to chain multiple .done(), .fail(), and .always() callbacks on a single request, and even to assign these callbacks after the request may have completed. If the request is already complete, the callback is fired immediately.

And, to your question:

Will throwing .promise() after every AJAX call that gets returned to the customer actually make things better for them in any way?

No, in my opinion, this will not help make it a better API as it will only serve to hide the jqXHR functionality and turn it into only a promise. The jQXHR object is already a promise object and not a deferred object so the promise aspect of protection against people mucking with the deferred is already there.


The only reason I can think of to return ONLY a promise object from your API would be if you're truly trying to hide the fact that you're using jQuery underneath so you don't want there to be any way for the API user to use any other jQuery features. But, unless you're also hiding ALL the argument functionality on the input to the Ajax call (so it can't look like jQuery on the input side of things either), then I see no point in hiding output functionality either. After all, it is jQuery underneath so why go to all the trouble of abstracting/redefining the whole jQuery ajax interface.

نصائح أخرى

Most best practices are invented not to get things done now, but to get things done further down the road as well. It all revolves around maintainability, reusability, flexibility etc. Requirements and technology change and your code should be robust enough to be able to adapt to those changes. The more tight coupling between components for instance, the harder it is to change a part without affecting other parts.

When designing a public facing API this is really important as well, probably even more so, since you should try not only making things easy for your future self, but also for all the users of your API. Backwards compatibility is one of these things users of an API really like, since it means they can upgrade your library w/o breaking any existing code. Obviously it's not always possible to make everything 100% backwards compatible (or sometimes it's not even desirable) It's a balancing act. Try to foresee possible future changes and code towards them. However this doesn't mean you should try jumping through hoops just because maybe one day this or that will change. You need to weigh the pros and cons and analyse the amount of work involved.

Now let's get to the question: If you return a jqXHR object from your method people will start using it not just as a promise, but as a full-fledged jqXHR object. They'll see it for what it is in the documentation, source code or their inspector and they _will_start using it as that. This means that their code expects not just a promise, but a jqXHR object, since that's what they are using. However this limits you the creator of the public API, because if one day down the road for whatever reason you don't want to return a jqXHR object you'll be making changes that aren't backwards compatible.

So let's assess how realistic this scenario is and what the solution is. Possible reasons why you might not be returning a jqXHR object in the future include (but are not limited to):

  • What if you decide to use a different library instead of Jquery (for this method)?
  • What if you need/want a different retrieval mechanism? For instance instead of retrieving data through ajax, you want to retrieve it from LocalStorage. Or maybe both?
  • What if you want to wrap jquery promises with a promise library (for instance Q allows you to do this, since jquery promises aren't proper promises)?
  • What if the flow changes and this method is only the first in a sequence of distributed processors?

In all of the above cases you will either jump through hoops (like for instance passing a stale jqXHR object around) to avoid breaking backward compatibility or you'll just break it. In all of the above cases people that have been using the returned object not only as a promise, but as a jqXHR object will have to change their code. This can be quite a substantial change.

Now let's get back to the balancing thing. None of the above scenario's are a concern if the solution to avoid some of the potential headaches would be convoluted or elaborate, but it's not. It's just one simple thing:

return jqXHR.promise();

The implementation detail is abstracted away, people don't know and especially can't rely on one specific implementation detail. I can almost guarantee you that it will save you trouble.

Many people seem to have a kneejerk reaction when it comes to best practices: "but it's totally possible to do it with [other solution]". Sure. Nobody's saying it's impossible or insurmountable. You just try to keep things easy both now and in the future.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top