Should a JS API Convert jqXHRs to Promises? - javascript

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.

Related

Does XMLHttpRequest.onreadystatechange still gets called under the hood when we use Promises?

Let us say I am using a Promisified API for making an Ajax Request, even then the event-loop would still call the plain old XMLHttpRequest.onreadystatechange right?
I mean Promisfying is a nice way to write code in a sequential style, but under the hood what happens is still the plain old mechanisms right? A bit like the class syntax in ECMASCRIPT 6. In other words, the core API's are still the same?
I can't see how we can register a Promise directly to an event-loop, because there should be some logic should be there which would "resolve" or "reject", so a simple wrapper has to exists [that can be implemented a promise too, I don't know if it would lead to too much promises]?
Let us say I am using a Promisified API for making an Ajax Request, even then the event-loop would still call the plain old XMLHttpRequest.onreadystatechange right?
There are several ways to make Ajax requests.
A library which wrapped a promise around XHR could use a readystatechange event. It could use load and error events.
A promise-based Ajax library could avoid XHR entirely.
It could use JSONP.
fetch is supported natively in browsers, is promise based, and doesn't go near XHR.
A bit like the class syntax in ECMASCRIPT 6. In other words, the core API's are still the same?
No.
Promises are a standardised API, not just different syntax to do the same things as existing code.

How to retrieve native function code after it was redefined by third-party js?

I'm writing a userscript for tumblr that makes ajax calls to its API over an array of data and I need the responses to come in the order the data was in the array. I was advised to use Promises for that as the article shows (http://www.html5rocks.com/en/tutorials/es6/promises/#toc-parallelism-sequencing).
It works on individual tumblr blog pages, but not on the dashboard, where native implementation of Promise is overwritten by their index.js. As the result, their Promise lacks .resolve() ability and my code doesn't work.
Can I still find a way to use the native Promise or do I have to seek other ways? I'd prefer to stick with Promises to avoid having different code for a single use case. Alternatively, I would have to use jQuery for same functionality and I don't really know how.
Right now I'm solving this by force-loading of Promise polyfill from GitHub, but this doesn't feel right.

What's a clean way to handle ajax success callbacks through a chain of object methods?

So, I'm trying to improve my javascript skills and get into using objects more (and correctly), so please bear with me, here.
So, take this example: http://jsfiddle.net/rootyb/mhYbw/
Here, I have a separate method for each of the following:
Loading the ajax data
Using the loaded ajax data
Obviously, I have to wait until the load is completed before I use the data, so I'm accessing it as a callback.
As I have it now, it works. I don't like adding the initData callback directly into the loadData method, though. What if I want to load data and do something to it before I use it? What if I have more methods to run when processing the data? Chaining this way would get unreadable pretty quickly, IMO.
What's a better, more modular way of doing this?
I'd prefer something that doesn't rely on jQuery (if there even is a magical jQuery way), for the sake of learning.
(Also, I'm sure I'm doing some other things horribly in this example. Please feel free to point out other mistakes I'm making, too. I'm going through Douglas Crockford's Javascript - The Good Parts, and even for a rank amateur, it's made a lot of sense, but I still haven't wrapped my head around it all)
Thanks!
I don't see a lot that should be different. I made an updated version of the fiddle here.
A few points I have changed though:
Use the var keyword for local variables e.g., self.
Don't add a temporary state as an object's state e.g., ajaxData, since you are likely to use it only once.
Encapsulate as much as possible: Instead of calling loadData with the object ajaxURL, let the object decide from which URL it should load its data.
One last remark: Don't try to meet requirements you don't have yet, even if they might come up in the future (I'm referring to your "What if...?" questions). If you try, you will most likely find out that you either don't need that functionality, or the requirements are slightly different from what you expected them to be in the past. If you have a new requirement, you can always refactor your model to meet them. So, design for change, but not for potential change.

Best practise for context mode at runtime in JS

I have a web application based on apache. php, js and jquery. All works fine.
On the client side there is a small library in JS/jquery, offering some generic list handling methods. In the past I used callbacks to handle those few issues where those methods had to behave slightly different. That way I can reuse methods like list handling, dialog handling and stuff for different part of the application. However recently the number of callbacks I had to hand through when stepping into the library grew and I am trying a redesign:
Instead of specifying all callbacks as function arguments I created a central catalog object in the library. Each module of the application registers its own variant of callbacks into that catalog upon initialization. At runtime the methods lookup the required callbacks in that catalog instead of expecting it specified in their list of arguments. This cleans up things quite a lot.
However I have one thing I still cannot get rid of: I require a single argument (I call it context, mode might be another term) that is used by the methods to lookup the required callback in the catalog. This context has to be handed through to all methods. Certainly better than all sorts of different callbacks being specified everywhere, but I wonder if I can get rid of that last one to.
But where do I specify that context, if not as method argument ? I am pretty new to JS and jquery, so I failed to find an approach for this. Apparently I don't want to use global vars, and to be frank I doubt that I can simply store a context in a single variable anyway, since because of all the event handlers and external influences methods might be called in different contexts at the same time, or at least interleaving. So I guess I need something closer to a function stack. Maybe I can simply push a context object to the stack and read that from within the layers of the library that need to know ? The object would be removed when I leave the library again. Certainly other approaches exist too.
Here are so many experienced coders how certainly can give a newbie like a short hint, a starting point that leads to an idea, how to implement this. How is such thing 'usually' done ?
I tried round a while, exploring the arguments.callee.caller hierarchy. I thought maybe I could set a prototype member inside a calling function, then, when execution steps further down I could simply traverse the call stack upwards until I find a caller holding such property and use that value as context.
However I also saw the ongoing discussions that reveal two things: 1.) arguments.callee appears to be depreciated and 2.) it appears to be really expensive. So that is a no go.
I also read about the Function.caller alternative (which appears not to be depreciated and much more efficient, however until now I failed to explore that trail...
As written currently passing the context/mode down simply works by specifying an additional argument in the function calls. It carries a unique string that is used as a key when consulting the catalog. So something like this (not copied, but written as primitive example):
<!-- callbacks -->
callback_inner_task_base:function(arg1,arg2){
// do something with args
}
callback_inner_task_spec:function(arg1,arg2){
// do something with args
}
<!-- catalog -->
Catalog.Callback:function(context,slot){
// some plausibility checks...
return Catalog[context][slot];
}
Catalog.base.slot=callback_inner_task_base;
Catalog.spec.slot=callback_inner_task_spec;
<!-- callee -->
do_something:function(arg1,arg2,context){
...
// callback as taken from the catalog
Catalog.Callback(callback,'inner_task')(arg1,arg2);
...
}
<!-- caller -->
init:function(...){
...
do_something('thing-1',thing-2','base');
do_something('thing-1',thing-2','spec');
...
}
But where do I specify that context, if not as method argument ?
Use a function property, such as Catalog.Callback.context
Use a monad

jquery ajax filter chain

I'd like to have a chain of filters (mostly in cases of errors) which are called sequentially and are given the xhrObject, so that each filter function can decide what to do based on the specific fault. There should also be a mechanism for passing along the data to the next filter function, or stopping the chain at some point.
I know that the Deferred objects in jQuery allow something like that, but I don't really see a way to declare one global xhrObject, to which these filter callbacks will hook up at the very beginning, so that they are handling any response/fault. Besides, having one global service delegate is not good either, because it may make the app go out of sync if the users perform many operations, while the previous ones haven't finished yet.
Besides, having one global service delegate is not good either,
because it may make the app go out of sync if the users perform many
operations, while the previous ones haven't finished yet.
AJAX, by its nature, is asynchronous. You should program accordingly. There is nothing wrong with a single delegation AJAX function.
I'd like to have a chain of filters
I think you need to explain this portion a little more clearly.

Categories

Resources