Посмотрите, как выглядит «текстовый чат» по протоколу HTTP, когда клиент запрашивает у сервера что-то, и получает ответ.
Примерная очерёдность:
Код на клиенте вызывает 
fetch(). Устанавливается соединение, передаётся запрос «дай /api/method» и передаются заголовки, типа «готов принять json».
Сервер обрабатывает, думает и «пишет в чат»:
1) Строку со статусом 
HTTP/1.1 200 OK – мол, норм, ща всё будет.
2) Несколько строк HTTP-заголовков ответа: размер, тип и пр.
Пустую строку – мол, заголовки всё.
Вот в этот момент 
fetch уже понимает, как у него дела с этим запросом. И ресолвит первый промис. Уже ясно, что вроде всё ок, ща польют данные, и это надолго. Дальше вступает в работу объект Response.
3) После пустой строки после заголовков ответа, сервер начинает лить данные тела ответа. Может пару букв, а может гигабайт дампа. Представьте медленный интернет, зарезанную скорость «недружественного» сайта и т.п.
Данные медленно ползут... И наконец, полностью переданы-получены.
Вот тут у объекта 
Response ресолвится его 
.json() промис. Посмотрите по ссылке — у того же объекта (мы его получаем после первого ресолва 
fetch()) есть и свойства, доступные сразу же, синхронно: например, объект с заголовками: свойство 
headers. Или свойство 
ok, значение которого следует из самой первой строки ответа сервера.
В общем, понять асинхронность помогает (мысленное) замедление всего и вся. Как будто интернет медленный, вычисления выполняются вручную на бумажке с калькулятором.   Становится ясно, что какие-то потенциально долгие делишки ждать невесело, поэтому их делают асинхронными: сразу возвращают Promise, который отресолвится когда там всё доделается. А пока ждём, можно и салат оливье нарезать )