45

When requesting from a server with JavaScript fetch API, you have to do something like

fetch(API)
  .then(response => response.json())
  .catch(err => console.log(err))

Here, response.json() is resolving its promise.

The thing is that if you want to catch 404's errors, you have to resolve the response promise and then reject the fetch promise, because you'll only end in catch if there's been a network error. So the fetch call becomes something like

fetch(API)
  .then(response => response.ok ? response.json() : response.json().then(err => Promise.reject(err)))
  .catch(err => console.log(err))

This is something much harder to read and reason about. So my question is: why is this needed? What's the point of having a promise as a response value? Are there any better ways to handle this?

The Guy with The Hat
  • 10,836
  • 8
  • 57
  • 75
amb
  • 4,798
  • 6
  • 41
  • 68
  • 4
    The response object is not a promise. It's a response, with a `json` (and method among others which returns a promise. Is your question why `json()` returns a promise? –  Sep 22 '15 at 16:24
  • 1
    Not exactly a duplicate, but you might want to look [here](http://stackoverflow.com/a/32516463/3887516) – Amit Sep 22 '15 at 17:15
  • Do you really need the content of the 404 response as your error? If not, there are simpler ways to achieve what you want. If you do, I don't see anything wrong with how `fetch` works. – Bergi Sep 22 '15 at 17:48
  • Yes, I need that. There's nothing wrong with fetch, just that I wanted to know how it works under the hood. – amb Sep 22 '15 at 18:04
  • I find this conversation (response promise, fetch promise??) confusing. There is only one promise object - that returned by `fetch()`. This promise's `.then` method maps it to the return value of the function passed to `.then`. In the first case, the function returns with `response.json()`. Since this is a value, not a promise, the original promise gets resolved with this value. In the second case, `.then` [conditionally] maps the original promise to a rejected promise, so the original promise gets rejected with whatever cause was passed to the rejected promise. Am I missing something? – caasjj Sep 22 '15 at 21:06
  • 3
    @caasjj: `response.json()` does return a promise as well (as it waits for the body to load) – Bergi Sep 22 '15 at 21:19
  • @Bergi - thanks. found it (https://developer.mozilla.org/en-US/docs/Web/API/Body/json), for anyone else equally confused. – caasjj Sep 22 '15 at 21:26
  • @torazaburo is right, my question is why ``response.json()`` returns a promise, I'll edit the question. @Bergi has answered why it returns a promise but I'll keep the question open for a while because I would really appreciate to see how people is using and reasoning about fetch responses. – amb Sep 23 '15 at 06:45
  • 1
    Does this answer your question? [Why does .json() return a promise?](https://stackoverflow.com/questions/37555031/why-does-json-return-a-promise) – Henke May 27 '21 at 16:04

2 Answers2

39

If your question is "why does response.json() return a promise?" then @Bergi provides the clue in comments: "it waits for the body to load".

If your question is "why isn't response.json an attribute?", then that would have required fetch to delay returning its response until the body had loaded, which might be OK for some, but not everyone.

This polyfill should get you what you want:

var fetchOk = api => fetch(api)
  .then(res => res.ok ? res : res.json().then(err => Promise.reject(err)));

then you can do:

fetchOk(API)
  .then(response => response.json())
  .catch(err => console.log(err));

The reverse cannot be polyfilled.

jib
  • 40,579
  • 17
  • 100
  • 158
4

Because somtimes we need a precise control for the loading process (from recieving the first piece of data to recieving the last one).

In actual world, json may not be a good example cause it's reletively samll. But imaging a situation where a large picture is loaded gruadually (from mosaic to clear). In that case, it is too late to inform the program when the data recieving has done completely.

Since fetch() is a relatively low level api, otherwise you could use axios or so on.

Tea_Drinker
  • 59
  • 1
  • 1