Why Two Awaits?
You’ve seen these two await
s before, right?
// first await
let response = await fetch("/some-url")
// second await
let myObject = await response.json()
We use asynchronous programming in the browser to handle tasks that take time to complete so that we don’t block the user interface.
Awaiting fetch
makes sense. We could be making a request to another continent, so we’d better not block the UI!
But, why on earth do we need to await
response.json()
?
Parsing JSON shouldn’t take a long time. In fact, we regularly call JSON.parse("{"key": "value"}")
which is a synchronous call. So, why does response.json()
return a promise instead of what we really want?
What’s going on?
From MDN’s article on the Fetch API
The fetch() method takes one mandatory argument, the path to the resource you want to fetch. It returns a Promise that resolves to the Response to that request — as soon as the server responds with headers — even if the server response is an HTTP error status.
So, fetch resolves the response before the body has necessarily been completely received.
Looking at the code from earlier:
let response = await fetch("/some-url")
// At this point,
// 1. the client has received the headers
// 2. the body is (probably) still making its way over.
let myObject = await response.json()
// At this point,
// 1. the client has received the body
// 2. the client has tried to parse the body
// as json with the goal of making a JavaScript object
It’s pretty obvious how useful it is to have access to the headers before the whole body arrives. Based on the status code, or one of the headers, we might decide to not read the body at all.
And the body arriving after the headers is actually something we’re used to. This is how everything works in the browser. HTML is slowly sent over the wire, as are images, fonts, etc. I guess I was just confused by the name of the method: response.json()
.
I have a simple node server that demonstrates this. The related code is all here.