My JavaScript book is out! Don't miss the opportunity to upgrade your beginner or average dev skills.

Tuesday, March 24, 2015

Ain't that fetch!

Update II
It turned out that Microsfot did what was the simplest way to provide Streams for developers, it simply used what was there already, and exposed within the readystatechange listener. That's a great, easy, and pragmatic solution for IE10 and above. I wish other vendors were like IE ... oh, irony!

Update I
Arthur Stolyar, aka @nekrtemplar, just explained in his gist why he's also disappointed about fetch. To cite his words:
I am not a "One particular high-profile JavaScript community member was unconvinced" or a spec's expert. I am a regular JavaScript/Front-end developer and this is how I see it from my point of view.
His technical explanation is even more detailed than mine so I suggest a read.
Chrome Canary is already exposing a fresh new Fetch API that has been empathized in this clarification post from @jaffathecake.
I've promised him I would have written a counter argument on why not everyone, or at least me, did applaud it.
I will try to answer point after point, providing also my point of view but before starting it's obligatory to underline that nobody is trying to block anything but hopefully the entire thing will be rolled out complete and ASAP with all details in.

Events VS Promises

The first provided example in Jake page talks about a massively improved API. It shows some basic XHR logic I'd like to reproduce under a different light:
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = () => console.log(xhr.response);
xhr.onerror = () => console.log("Booo");
xhr.send();
Above 5 lines of code have been described in the original, not arrow->function, example as some vomit to mop up 'cause the new thing looks way better:
fetch(url)
  .then(r => r.json())
  .then(data => console.log(data))
  .catch(e => console.log("Booo"))
;
The example ends up in a rather confusing block for ES7, not sure this is a "back to vomit" thing or not ...
(async() => {
  try {
    var response = await fetch(url);
    var data = await response.json();
    console.log(data);
  } catch (e) {
    console.log("Booo")
  }
})();
If we talk about lines of code, fetch wins, but if we talk about clarity of intent can I say XHR has the best?
  1. it's a GET, not a POST, PUT, HEAD, or others, it's clearly and explicitly a GET
  2. it comes with listeners, meaning it has onprogress included for bigger data
  3. it comes with .abort() as explicit intent, and the ability to add an onabort in order to react
I will come back to the latter point but here we have yet another competition between Events and Promises, when of course Promises, on top of events, can be used to generated simplified APIs, but comparing them is like comparing apples and oranges: Promises and Events are not mutually exclusive patterns, it's the exact contrary: these can be used together, and indeed, being events more suitable for recurring invocation of the same callback during some streaming operation, it's not that we can blame Events to have an ugly API there, we simply need them.
My point here is that wrapping XHR through a Promise was already possible and it has been done already so this does not seem to be a reason to applaud this latest API, no clap clap here, I am sorry, not for this.

It isn't done yet

When I've read this part:
What exists in Chrome today doesn't cover the full spec, and the spec doesn't cover all of the planned features. In some cases this is because they haven't been designed yet, in others it's because they're dependent on other in-progress specs.
I could not help myself thinking about a tweet of mine, posted few days ago:
@dfkaye what I think is that first few lines of any new proposed Standards body API should be about "what real-world problem is this solving"
So I'd like to understand if new APIs just come out "because" or if there are real reasons behind for pushing these out.
I am 100% sure that WHATWG does its best, it works hard as a group, and the aim is to provide top notch solutions for our daily needs, but honestly ... to quickly answer to the following Jake disappointment:
Bit of a rant: it bothers me that as developers, we preach iterative development and release, but when we're the customers of that approach the reaction is all too often "HOW DARE YOU PRESENT ME WITH SUCH INCOMPLETE IMPERFECTION".
The way I felt about this was rather like the following: (which is just a satiric parody in my mind)
  • Some guru at WHATWG: we need a better version of XHR
  • Her colleague: Totally, how should we start?
  • Some guru at WHATWG: let's think about a simplified entry-point API to start with
  • Her colleague: Totally, gotta Fetch em'all via Promises!!!
  • Some guru at WHATWG: well, actually something that .. you know ... Network, something cancelable by default ...
  • Her colleague: Totally, gotta Fetch em'all via Generators!!!
  • Some guru at WHATWG: ... yeah right ... let's stick with the first idea that breaks only the Promises principles in ES6 ... and re-iterate down the road
And again, the way I see that picture is like:


Because the thing has been planned from the scratch without breaks in mind!

Having the ability to stop a download, a streamed content, or an upload for the wrong image, should have been the very first thing to think about for a new Network based API, riight?
I am simply astonished the entry point for the new XHR replacement has already a somehow limited/broken heart since the beginning and for the entire ES6 era, because as far as I know native Promises cannot be canceled (yet, 'cause they will eventually ... they have to!)
I wonder then if this is really the right way to roll out new APIs ... so that we are forced to rush solutions in order to be able to improve also a core problem in Promise-land?
Where was the announcement that a new Network API based on Promise was coming? Have I missed that? The answer is probably yes, since it's even in the specification, but then can I say I wasn't again see that coming at all?

Developers VS Standards

To keep answering Jake disappointment, as a developer I want to trust that standard bodies will choose the right tool for the job.
I like all open channels, I like the iterative model, but I don't honestly have time to be the supervisor for everything so my apologies if I haven't read every single thing people paid to write out published.
As a developer, I'd also like to trust standard bodies that if I've learned something already, like the XHR Level 2 API, and if an API has already been updated for a nice to have upgrade as onload and onprogress were, maybe just a tiny extra update to bring onstreaming would have made such API even better and we could have let libraries and wrappers deal with the best approach to Promisify all the things and implement once de-facto standardized (obligatory mention to querySelectorAll indeed).
This is not me complaining about nicer, easier looking APIs, this is me wondering what's the rush about and why so many steps at once as in the Fetch API and everything else around it.

Not All Disappointing After All

I've hopefully explained why at the very beginning I could not believe to my eyes but also thanks to Jake I've understood there are surrounding parts that come somehow for free like Streams, readers, no-cors, ServiceWorkers integration ( through events and addEeventListener :trollface: ) and more.
I honestly wish Streams where usable directly as row level API and probably that's going to happen or it happened already, and now that I know about all these things around "just fecth" I can't wait for all this to be fully out and not just a little bit, 'cause as it is, it looks Promising (dehihihi) but it's not worth using it yet due limitations, specially on the current initial cancel-ability (partially in or partially buggy here and there).

About XHR: Yes, It Is Still Good!

If we had a time to improve it, that wouldn't be 16 years ago ... that was Microsoft, not XMLHttpRquest, so I am not sure why Jake said that in his blogpost since first working drafts seem to be dated 2006.
However, XHR has been revolutionary for the entire Web and it also made eventually people aware of how the internet was working: not everything is a GET, you could PUT, POST, HEAD, and it has been used to create everything cool today about Google services, social networks etc etc.
It's already easy to do what it can do, so I wouldn't say it has been simplified because of a .then.
I can also still upload and drop while uploading images in Gmail, or TweetDeck, or somewhere else and entirely through XHR. I can post data, show progress bars, I can fetch JSON and do CORS. I can do pretty much everything but controlling streams ... and you know what? I think XHR deserves a bit of extra respect.
I wouldn't spit on something that is almost 100% consistently adoptable these days and without libraries, when most of the people just used it to GET some JSON and nothing else (reasons Promise like based Ajax calls became popular, most developers use 10% of XHR ability) for the last 10 years.
Accordingly, if there is one thing I absolutely don't like about that post, is the following sentence:
XHR was an ugly baby and time has not been kind to it. It's 16 now. In a few years it'll be old enough to drink, and it's enough of a pain in the arse when it's sober.
Come on Jake, XHR is still amazing, easy as hell to use, and it still has a lot to offer: abortability with optional reaction through listeners, good integration with Form data, progression, multiple listeners so it can notify multiple "chains" and everything else we know and used for the last 16 years indeed. If it was that bad, I am pretty sure it would have disappeared way before, and the Web as we know would have probably been behind ... don't you think?
Cheers

4 comments:

Jake Archibald said...

With such a simple example, yes, XHR can get close to fetch in terms of compactness. But say you wanted to wait until 3 XHR's complete, or add 3 XHR results to the page as soon as possible, but maintaining order, then the XHR example is going to look worse and worse compared to fetch.

Holding up an explicit "GET" as a benefit is weird unless you're also going to complain about all the stuff XHR doesn't make explicit. These include async, responseType, withCredentials - if it makes sense to have defaults here, why must method be explicit.

In fact, XHR doesn't even have consistent defaults. withCredentials is true for same-origin, false otherwise. Fetch on the other hand uses sensible and consistent defaults.

onprogress is a high level feature. With fetch you can use streams to track progress, which encourages you to look at the response, check if it has a content-length, and be prepared for that to be absent, or a lie.

I don't get the point of the made-up dialog between WHATWG and her colleague. Is this suggesting how fetch was designed? If so it's kinda offensive and trolly.

"Having the ability to stop a download, a streamed content, or an upload for the wrong image, should have been the very first thing to think about for a new Network based API, riight?" - and it was. Fetch has been designed around streaming from the start, which you'll know if you read my post. You'll also see the example of terminating a stream, which aborts the download.

Aborting an upload hasn't been spec'd yet, but it will be. Iterative design, as I say.

"Were was the announcement that a new Network API based on Promise was coming?" - yes, this was developed in the open, discussed in mailing lists, on the Chrome site there were intents to implement and to ship, I've written articles on it, given talks, as have others. Where should this announcement have been so you wouldn't have missed out?

"this is me wondering what's the rush" - have you seen how native is kicking our arse at the moment? We owe developers a better platform. ServiceWorker brings use network control, and the foundation for push messaging and background sync. We need an uncoupled Request/Response model for this, we need proper streaming, not something hacked on top of a rusty old API.

"I honestly wish Streams where usable directly as low level API" - they are. If you read my post, you'd see the link to http://streams.spec.whatwg.org.

"I am not sure why Jake said that in his blogpost since first working drafts seem to be dated 2006" - as you well know, the design of XHR dates much earlier than that.

Andrea Giammarchi said...

1. you can easily wrap XHR via ES6 Promise, problem solved for the 3 requests

2. defaults are easy to make since about ever in JS

3. the check for the content length can be done in onprogress too or eventually a HEAD call

4. the dialog key is: which entry point is not cancelable? "Promises" ... yeah, let's choose it. If that's not what happened, please explain me what happened. Anyway, it's a humanistic personal vision of what happened, same as the image I've revisited. And since that's not what truly happened (right?) nobody should get offended, I guess, for real ... why? Or better: Where is the explanation of WHY a non cancelable API as wrapper was chose in the first place? That one, I miss it.

5. things not specd' yet but will are 50% of the reason I wrote this post. What does that even mean when you plan to push out a new API?

6. I'm sorry you stopped at the question and didn't kept reading the part I've said it was my fault missing it and why.

7. with all problems and lacks behind Native we have, I am pretty sure a half backed API is not the answer. We still need so much lower level API access to devices I've no idea why somebody decided XHR was the real problem.

8. I've read your post, haven't seen an example and couldn't test them on Canary. I will try again

9. no, XHR dates 2006, what Microsoft did dated before and it didn't have to become a standard in that way. However, it was already good enough, that's why it got adopted. Why blaming XHR and 16 years of honorable work? I don't understand that, it felt a bit forced to me

Andrea Giammarchi said...

humanistic => humorist

Andrea Giammarchi said...

Also, just to clarify, this blog comments don't go until I confirm the source is safe so, in case you answer, please wait for me to publish.

I really hoped you would have taken this post as "just fair", because everything you read is simply what I've thought and me being simply honest.

You put funny GIFs and what you think people think or say or complain about as well in you posts, and we hopefully still have sarcasm and satire post ability online.

And about this: I haven't made names, beside yours about your post, without putting you in any satiric situation, and I haven't insulted directly anyone.

I hope we can keep it that way.