Введение в fetch()

fetch() позволяет отправлять сетевые запросы, аналогичные XMLHttpRequest (XHR). Основное отличие заключается в том, что Fetch API использует Promises, у которого более простой API, помогающий избежать сложных обратных вызовов в XMLHttpRequest API.

Browser Support

  • Chrome: 42.
  • Edge: 14.
  • Firefox: 39.
  • Safari: 10.1.

Source

Если вы никогда не использовали Promises, ознакомьтесь с Введение в JavaScript Promises.

Простой запрос Fetch

Ниже приведен пример реализации с использованием XMLHttpRequest, а затем – с использованием fetch. Мы хотим запросить URL-адрес, получить ответ и проанализировать его как JSON.

XMLHttpRequest

Для XMLHttpRequest требуются два прослушивателя, которые будут обрабатывать успешные и неудачные попытки, а также вызовы open() и send(). Пример из документации MDN.

function reqListener () {
  const data = JSON.parse(this.responseText);
  console.log(data);
}

function reqError (err) {
  console.log('Fetch Error :-S', err);
}

const oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();

Получить

Наш запрос на получение выглядит следующим образом:

fetch('./api/some.json')
  .then(response => {
    if (response.status !== 200) {
      console.log(`Looks like there was a problem. Status Code: ${response.status}`);

      return;
    }

    // Examine the text in the response
    response.json().then(function(data) {
      console.log(data);
    });
  })
  .catch(err => {
    console.log('Fetch Error :-S', err);
  });

Для выполнения той же задачи, что и в примере с XHR, в запросе fetch() требуется только один вызов. Чтобы обработать ответ, мы сначала проверяем, что его статус равен 200, а затем анализируем ответ как JSON. Ответом на запрос fetch() является объект Stream. Это означает, что после вызова метода json() возвращается Promise. Поток выполняется асинхронно.

Метаданные ответа

В предыдущем примере показан статус объекта Response и то, как анализировать ответ в формате JSON. Вот как можно получить доступ к другим метаданным, например заголовкам:

fetch('users.json').then(response => {
  console.log(response.headers.get('Content-Type'));
  console.log(response.headers.get('Date'));

  console.log(response.status);
  console.log(response.statusText);
  console.log(response.type);
  console.log(response.url);
});

Типы ответов

Когда мы отправляем запрос на получение, ответу присваивается response.type со значением basic, cors или opaque. Эти types показывают, откуда поступил ресурс, и вы можете использовать их, чтобы определить, как обрабатывать объект ответа.

Когда браузер запрашивает ресурс из того же источника, ответ имеет тип basic с ограничениями на то, что вы можете просматривать в ответе.

Если запрос отправлен ресурсу из другого источника, который возвращает заголовки CORS, то тип будет cors. Ответы cors похожи на ответы basic, но ограничивают заголовки, которые вы можете просматривать, до Cache-Control, Content-Language, Content-Type, Expires, Last-Modified и Pragma.

Ответы opaque поступают из другого источника, который не возвращает заголовки CORS. При непрозрачном ответе мы не сможем прочитать возвращенные данные или просмотреть статус запроса, а значит, вы не сможете проверить, был ли запрос успешным.

Вы можете определить режим для запроса на получение, чтобы разрешались только определенные типы запросов. Доступны следующие режимы:

  • same-origin успешно обрабатывает только запросы объектов из того же источника и отклоняет все остальные запросы.
  • cors разрешает запросы объектов в том же источнике и других источниках, которые возвращают соответствующие заголовки CORS.
  • cors-with-forced-preflight выполняет предварительную проверку перед отправкой запроса.
  • no-cors предназначен для отправки запросов к другим источникам, у которых нет заголовков CORS, и получения ответа opaque, но, как уже говорилось, в настоящее время это невозможно в глобальной области видимости окна.

Чтобы задать режим, добавьте объект options в качестве второго параметра в запрос fetch и определите режим в этом объекте:

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
  .then(response => response.text())
  .then(text => {
    console.log('Request successful', text);
  })
  .catch(error => {
    log('Request failed', error)
  });

Цепочка обещаний

Одна из замечательных особенностей обещаний – возможность объединять их в цепочки. Для fetch() это позволяет использовать общую логику в запросах на получение.

Если вы работаете с JSON API, вам нужно проверять статус и анализировать JSON для каждого ответа. Вы можете упростить код, определив статус и синтаксический анализ JSON в отдельных функциях, которые возвращают промисы, и использовать запрос fetch для обработки только конечных данных и случая ошибки.

function status (response) {
  if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
  } else {
    return Promise.reject(new Error(response.statusText))
  }
}

function json (response) {
  return response.json()
}

fetch('users.json')
  .then(status)
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  }).catch(error => {
    console.log('Request failed', error);
  });

В этом примере определяется функция status, которая проверяет response.status и возвращает либо распознанный объект Promise как Promise.resolve(), либо отклоненный объект Promise как Promise.reject(). Это первый метод, вызываемый в цепочке fetch().

Если объект Promise разрешается, скрипт вызывает метод json(), который возвращает второй объект Promise из вызова response.json() и создает объект, содержащий обработанный JSON. Если синтаксический анализ не удается выполнить, объект Promise отклоняется и выполняется оператор catch.

Такая структура позволяет использовать логику во всех запросах на получение, что упрощает поддержку, чтение и тестирование кода.

запрос POST

Иногда веб-приложению требуется вызвать API с помощью метода POST и включить в тело запроса некоторые параметры. Для этого задайте параметры method и body в настройках fetch():

fetch(url, {
    method: 'post',
    headers: {
      "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
  })
  .then(json)
  .then(data => {
    console.log('Request succeeded with JSON response', data);
  })
  .catch(error => {
    console.log('Request failed', error);
  });

Как отправить учетные данные с запросом на получение

Чтобы выполнить запрос на получение с учетными данными, например файлами cookie, задайте для параметра credentials значение "include":

fetch(url, {
  credentials: 'include'
})