В каждом современном браузере есть встроенная функция fetch(). Она делает HTTP-запрос и возвращает результат. Это базовый способ работы с API из JavaScript — библиотеки вроде Axios всё ещё популярны, но под капотом они теперь чаще всего вызывают тот же fetch.

Самый простой пример

Минимальный запрос выглядит так:

fetch('https://jsonplaceholder.typicode.com/posts/1')
  .then(response => response.json())
  .then(data => console.log(data));

Три строки. Сделали GET-запрос. Прочитали тело как JSON. Вывели в консоль. Можно прямо сейчас открыть консоль браузера на любой странице и вставить эти строки — в консоли появится объект с полями userIdidtitlebody.

JSONPlaceholder — это бесплатный публичный API специально для тренировки и демо. Не требует регистрации, не требует токена, у него есть посты, пользователи, комментарии. Подробно про него — в главе 4.4.

Что такое Promise

В коде выше есть два метода .then(). Что это?

HTTP-запрос — асинхронная операция. Браузер отправляет запрос и не ждёт ответа на месте: он продолжает работать с другими делами, а когда ответ придёт — вернётся к нашему коду. Чтобы как-то обозначить «то, что появится в будущем», в JavaScript есть конструкция Promise.

fetch() возвращает не сам ответ, а обещание ответа — объект типа Promise. К этому обещанию можно прикрепить функцию через .then(): «когда ответ всё-таки придёт, выполни вот это».

Аналогия: в кафе вам дают пейджер вместо готового кофе. Сам кофе придёт через три минуты. Пейджер — это Promise. Когда он замигает (Promise «разрешится») — вы пойдёте к стойке за заказом. Условный код:

const pagerForCoffee = orderCoffee();        // Promise
pagerForCoffee.then(coffee => drink(coffee)); // что делать, когда придёт

Два .then() подряд: почему

В исходном примере два .then():

fetch(url)
  .then(response => response.json())  // ← первый
  .then(data => console.log(data));   // ← второй

Это потому, что чтение тела ответа тоже асинхронное. Когда fetch завершился, в руках у нас объект Response — в нём есть статус, заголовки, и сырое тело. Тело может прийти кусками или быть большим, поэтому его чтение тоже Promise:

  • response.json() — читает тело и парсит как JSON. Возвращает Promise с JS-объектом.
  • response.text() — читает тело как обычную строку.
  • response.blob() — читает как бинарный объект (для картинок, файлов).

Итого: первый .then() ждёт, когда придут заголовки и можно начинать читать. Второй ждёт, когда тело прочитается и распарсится. Это и есть классическая «лесенка из .then()».

В главе 4.3 разберём, как переписать то же самое короче через async/await.

Живая демка

Здесь — самый базовый пример: запросить один пост и вывести его в страницу.

const url = 'https://jsonplaceholder.typicode.com/posts/1';

fetch(url)
  .then(response => response.json())
  .then(post => {
    document.querySelector('#title').textContent = post.title;
    document.querySelector('#body').textContent  = post.body;
  });

Проверка response.ok — обязательно

В коде выше есть скрытая ловушка. Что если сервер вернёт 404 или 500? fetch по умолчанию не считает HTTP-ошибки исключениями. Promise разрешится успешно даже на 404. Внутри response.json() произойдёт попытка распарсить тело ошибки как JSON — и в зависимости от того, что прислал сервер, это либо упадёт, либо вернёт неожиданные данные.

Правильный паттерн:

fetch(url)
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }
    return response.json();
  })
  .then(post => render(post))
  .catch(error => showError(error.message));

response.ok — это true, если статус 2xx. На 3xx,4xx, 5xx это false. Подробный разговор про ошибки и про этот .ok-нюанс — в модуле 6, целиком ему посвящённом. Сейчас просто запомните: после fetch первым делом проверяем response.ok.

Что мы только что сделали

  1. Отправили HTTP GET-запрос на публичный API.
  2. Дождались ответа через Promise.
  3. Прочитали тело как JSON.
  4. Распарсенный объект вывели в страницу.

Это базовый кирпич, из которого собрано почти всё взаимодействие современных сайтов с API. Дальше только модификации: другой URL, другой метод, другие данные в теле, другие заголовки.

Поддержка браузерами
chrome
Chrome
42
firefox
Firefox
39
edge
Edge
14
safari
Safari
10.1
opera
Opera
29