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