Hacker Newsnew | past | comments | ask | show | jobs | submitlogin
Ajax Battle: XMLHttpRequest vs. the Fetch API (openreplay.com)
63 points by ceeb on April 19, 2022 | hide | past | favorite | 58 comments


> XMLHttpRequest always sends browser cookies

No:

The XMLHttpRequest.withCredentials property is a boolean value that indicates whether or not cross-site Access-Control requests should be made using credentials such as cookies, authorization headers or TLS client certificates.

https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequ...


The blog post seems to only be referring to same-site requests for XHR, whereas your response is only referring to cross-site requests. A more complete correction would be:

XMLHttpRequest always sends browser cookies for same-origin requests. They are not included for cross-origin requests unless explicitly requested. Fetch does not send cookies by default for either type of request, unless explicitly requested.

Also note that the server must use the Access-Control-Allow-Credentials header for the response to be made available to the client code when making a cross-origin request with credentials.

https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Ac...


Even more important: it's value is `false` by default.


Who decides what is “cleaner” and “simpler”?

It’s such a strange take in the coding world. I don’t know why other developers get to decide what is clean, simple, easier to understand, etc, when it comes to my code or the way I write code.

I’ve been using xmlhttprequest for YEARS and honestly, it’s pretty dang simple. It’s pretty darn clean. And when we see it, we know what it’s doing and how it works.

I’m not sure of the motivation behind changes and new API’s like this that keep trying to fix what really isn’t broken for most uses.


Ignoring the fact that XMLHttpRequest is a hideously unlovely API, it's a huge problem when your primary asynchronous abstraction is not implemented by your primary API for async IO.


That’s an opinion, not a fact, re: hideous etc.

Please explain the latter bit again.

I too see no need to switch suddenly to this fetch api when older browser support is important to my daily driver web app. I found the tone of this article condescending when browser apis have mega committee-ware battlescars all over them and likely implement both of xyz with the same call to epoll anyway, (joke about systems programming)


    if (xhr.readyState !== 4) return;
is gross and stateful. Certainly you can learn it and use it but

    .then((res) => res.json())
is much more declarative.


And if you forgot to call xhr.send() at the end. You screw up and your callback is never called. fetch doesn't even allow you to make this kind of mistakes.


It allows you take make other mistakes, such as using the wrong type for POST data resulting in "[object Object]" and similar gibberish being sent to the server.

Not saying XMLHttpRequest API's is brilliant, but fetch's API also doesn't exactly fill me with joy.


I think the key is the comparative "-er" in "cleaner" and "simpler." Fetch may not be the cleanest or simplest possible solution, but in my opinion it is cleaner and simpler. XMLHttpRequest is good. Fetch is better.


And I have the entirety of an XMLHttpRequest in my head anyway, that xhr.send(fd); is where you included your form data…

These strange notions of burden and beauty…


You don't need to check readyState; onload is only called when the readyState switches to 4.


At least fetch is a viable replacement for jQuery.ajax and the syntax is quite similar.


I had a web app that was making perhaps an unreasonable number of concurrent requests. Every now and then, pretty regularly like 1 in every 20 full page reloads it would fail somewhere along the line. I switched from the lib I was using to XMLHttpRequest directly and it worked reliably with little in the way of cluttering the code as I made my own minimal wrapper without the features I didn't need from the lib. That was quite a long time ago and the lib has likely had whatever the problem was resolved.


Generally if you can write your code in a way that uses scopes and returned values, it's cleaner and easier to maintain.

IE, this is why async / await was added to JavaScript: The code is significantly easier to read (cleaner) than promises.


I like that people are trying to show you examples of fetch vs xhr as though you haven't seen them. "If you just look at them, you'll clearly transform yourself to have the same aesthetic preferences I do!"

They're all missing the point, frankly. The point is, we're stuck in this vicious cycle where we all get selective amnesia and forget why someone was invented, and we decide that it's old/ugly/unergonomic/ungraceful/unclean/unsimple/etc./etc. and invent something new. Woe to them that love the fetch API, and fall into a time warp to 2005 when everyone was singing the praises of "AJAX". You'd be bowled over by the sheer culture shock. (Likewise, I can't wait for 2035, when we'll have invented some emoji-based way to represent fetches, and those still using fetch() or axios() will be seen as hipsters/clueless).


I haven't seen XMLHttpRequest used directly in non-legacy code in quite some time. If there is any battle, it's between fetch and axios[1], and fetch is winning by virtue of being standard. Node.js also decided to implement it.[2]

[1] https://github.com/axios/axios [2] https://blog.logrocket.com/fetch-api-node-js/


I still write XMLHttpRequest and see no reason to switch. I depend on things like progress notifications and cancelations which fetch has iffy support for.

https://github.com/magcius/noclip.website/blob/master/src/Da...


Progress is possible with fetch, you just need to read from the stream yourself. And since you're working with the stream, you can also do things like parse newline-delimited JSON as it downloads instead of all at once at the end. https://fetch-progress-demo.glitch.me/


Same here. Look how clean it is!

  with(console){
   with(new XMLHttpRequest()){
    onload=()=>log(responseText);
    onerror=error;
    open("GET", "https://..");
    send();
   }
  }
I'm only half joking.


My... eyes... For people less familiar with the subject:

    console.log(await fetch("https:/...").then(r => r.text()))
I agree progress reporting needs improvement though.


What do you mean by winning? As in usage?

Axios has many nice features (such as interceptors) and I think is just better than fetch, other than the fact that you need to add a few KB to your bundle of course.


Yes, I meant usage. I agree axios has many nice features but fetch comes with 90% of what you need for free. Also, its API is spreading on the "other side" too. Service workers, worker runtimes (Cloudflare etc.) and Deno all use the fetch API for handling requests (as opposed to sending them).


The only issue I have with Fetch is I can't get progress for up/downloading from it yet. Otherwise I prefer it over Ajax in every other way.


It’s also very difficult to save the received data to the browser as a file. It can be done with ugly hacks, but this should be supported natively in 2022. Background downloads are still a major PITA. Why?


I don't have a lot of front end experience, but the way I've done it in the past was with

    <a download href="example.com/my/download/route"></a>
and that always worked well for me. No idea if that would be helpful at all, I'm operating at a very surface level knowledge here.


That's the way to do if I want to just download a file. But the comments seem to be mentioning a scenario where they would like to download the file via Javascript.


You can do anything with the DOM in javascript - you could create an anchor node then download the file automatically via a.click()


That's how I do it now. And it's an ugly hack. Why is it not natively supported?


What would you consider a native support? ability to drop files on user drives without prompt?


No, of course not. I’m not talking about the user experience. I’m talking about native support in the fetch API to take downloaded data and save it to a file. User experience might be the user being prompted for a location, etc, just like now when saving a file through a hyperlink download.


I’m talking about saving data received from an asynchronous fetch response as a file on oval file system.


This. Until fetch has all the features of xhr, what's the point of being pretty?


I think fetch will never has all the features of `xhr`. At lease the sync `xhr` is not possible. The `promise` api don't even allow callback in `then()` to be called synchronized, let alone things like `fetch` that relies on it.


There are also fetch-like wrappers for xhr. Axios is a decent one, I think.


No sure if I’m missing something, but you can tap directly into the readable stream of fetch which should be at least as good as progress events? I’ve used it here (https://sqliteviewer.app/, click “load a sample”) and it works as expected.


Well, I have something new to try... (just glanced at your project)

If this works, why isn't this documented in an obvious manner on something like MDN?


Same for me, I just wonder how the standardization people could forget such a feature...


This is just one Ajax battle, many others were fought in the Trojan wars. I think we need some kind of JS-http thing called "Hector" which would be about as good as Ajax.


I always think it's about the football club


I always think it's the cleaning solution LOL


I mean there were even two Ajax's at Troy, Telamonian Ajax and Locrian Ajax.


Oh right, totally forgot about that. Telamonian Ajax (aka "Greater Ajax") is the one I'm thinking of.


Using flask and about 2 js functions, I'm able to make an ajax webapp in about 100 lines. It's really amazing.

Only problem is files.

I'm serving video files locally, and for some reason the server stops returning file requests at some point.

I was told to use a real webserver, but it doesn't seem trivial, at least on windows.


You can always wrap XHR with the success/error semantics you prefer with a Promise, or even, if you want to optimize, your own thenable interface.


XMLHttpRequest has a synchronous version, where as fetch does not.

For instance, this means you can do an XMLHttpRequest in your app's router, and decouple your presentation from dealing with endpoints. Fetch can't do this.


That's a very bad point to block your UI thread.

Your router should not block, and the UI on your next page should show a spinner or other loading indicator while it runs a fetch (or XmlHTTPRequest)


It's better for testing.


That's an unjustified excuse. The one thing that is consistent with UI programming among all platforms is that you never block the UI thread.

You should be able to mock fetch/xmlhttprequest at any point, not just in your router. You should be able to mock those calls without needing to block the UI thread at runtime.


No it's not. If you have to mock things like that it's a sign of poorly designed, stateful objects.


You mean unit testing? How so? I have never seen unit tests make real HTTP requests; it's much better to mock them. And changing from async to sync code for unit tests sounds like a bad idea, since you're not testing your code the way it actually works in production.


Your router could wait for async function and you could use await in your router's function, accomplishing the exact same thing but without blocking the main thread.


You could just await the async thing though, to make it synchronous.


You can use a async/await with fetch to force it to be synchronous.


async/await is not synchronous, it's promises under the hood


But from a programming standpoint, you are assured that the next induction Edie’s not execute until the response is received.


Functionally this is still very different from sync xhr. Changing from sync xhr to await+fetch will introduce new bugs into your application unless it was written carefully, because now code can run and interactions can happen during the fetch that would have been blocked by the sync xhr occupying the event loop.

Promises are also specced so that their handlers don't run until the event loop completes a turn, which means that no matter what there is now an opportunity for queued callbacks to run before you actually process the response to your request, even if it hit in-memory cache.


I would really love a synchronous spinMicrotasks() API, but for obvious reasons it's likely never happening.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: