fetch api

Одним из недостатков изпользования AJAX в вебе долго оставалось вот это - XMLHttpRequest - API не предназначенное для такого использования. Было много элегантных оберток вокруг XHR, но всегда казалось, что можно лучше.

XMLHttpRequest

XHR чересчур сложен на мой взгляд. Да и в целом непонятно, почему XML написано заглавными, а Http кэмел-кейсом. Но это не важно, вот как выглядит его использование:

if (window.XMLHttpRequest) { // Mozilla, Safari, ...
  request = new XMLHttpRequest();
} else if (window.ActiveXObject) { // IE
  try {
    request = new ActiveXObject('Msxml2.XMLHTTP');
  } 
  catch (e) {
    try {
      request = new ActiveXObject('Microsoft.XMLHTTP');
    } 
    catch (e) {}
  }
}

request.open('GET', 'http://test.com/ajax-endpoint', true);
request.send(null);

Конечно многочисленные библиотеки и фреймворки значительно упращали работу с этим... вот этим сверху.

fetch

Функция fetch доступна сейчас в глобальном объекте window, и первым аргументом туда передается адрес:

// url (обязательно), options (опционально)
fetch('http://test.com/ajax-endpoint', {
    method: 'get'
}).then(function(response) {

}).catch(function(err) {
    // Ошибка :(
});

Как и большинство современных апи, fetch API использует JavaScript Promises для работы:

fetch('http://test.com/ajax-endpoint').then(function(response) {

}).catch(function(err) {
    // Ошибка :(
});

// Чуть более "продвинутое" использование
fetch('http://test.com/ajax-endpoint').then(function(response) {
    return //...
}).then(function(returnedValue) {
    // ...
}).catch(function(err) {
    // Ошибка :(
});

Request Headers

Возможность добавить и/или изменить заголовки важная часть совеременных запросов. Для этого необходимо всего-лишь использовать new Headers():

// Создаем пустой объект заголовков
var headers = new Headers();

// Добавим парочку
headers.append('Content-Type', 'text/plain');
headers.append('X-My-Custom-Header', 'CustomValue');

// Вот гетеры и сетеры
headers.has('Content-Type'); // true
headers.get('Content-Type'); // "text/plain"
headers.set('Content-Type', 'application/json');

// Удаление заголовка
headers.delete('X-My-Custom-Header');

// Значения при инициализации
var headers = new Headers({
    'Content-Type': 'text/plain',
    'X-My-Custom-Header': 'CustomValue'
});

Можно использовать методы append, has, get, set и delete для работы с заголовками. Для того что бы работать с заголовками можно использовать Request :

var request = new Request('http://test.com/ajax-endpoint', {
    headers: new Headers({
        'Content-Type': 'text/plain'
    })
});

fetch(request).then(function() { /* обработка ответа */ });

Request

Request - это часть запроса в fetch. Передавая Request в fetch, можно использовать расширенные возможности запроса:

  • method - GET, POST, PUT, DELETE, HEAD
  • url - адрес запроса
  • headers - объект заголовков
  • referrer - отправитель запроса
  • mode - cors, no-cors, same-origin
  • credentials - надо ли отправлять куки вместе с запросом
  • redirect - follow, error, manual
  • integrity - integrity value
  • cache - тип кэширования (default, reload, no-cache)

Пример использования Request:

var request = new Request('http://test.com/ajax-endpoint', {
    method: 'POST', 
    mode: 'cors', 
    redirect: 'follow',
    headers: new Headers({
        'Content-Type': 'text/plain'
    })
});

// А теперь давайте используем!
fetch(request).then(function() { /* обработка ответа*/ });

Только адрес (URL) является обязательным.Все свойства становятся неизменяемыми после создания запроса. Важно помнить, что у Request есть метод clone, который придется кстати, когда надо использовать fetch внутри Service Worker API, - Request - это поток (stream), а его надо клонировать для потправки в fetch.

fetch также может создавать запрос самостоятельно:

fetch('http://test.com/ajax-endpoint', {
    method: 'POST', 
    mode: 'cors', 
    redirect: 'follow',
    headers: new Headers({
        'Content-Type': 'text/plain'
    })
}).then(function() { /* обработка ответа */ });

Response

Метод then возвращает объект Response, но также можно самостоятельно создать оюъект Response. Вот что можно настроить:

  • type - basic, cors
  • url
  • useFinalURL - Boolean если адрес будет конечным
  • status - статус (200, 404 и т.д.)
  • ok - Boolean для успешной обработки (статусы 200-299)
  • statusText - статус ответа (например OK)
  • headers - объект заголовков.
var response = new Response('.....', {
    ok: false,
    status: 404,
    url: '/'
});

fetch('http://test.com/ajax-endpoint')
    .then(function(responseObj) {
        console.log('status: ', responseObj.status);
    });

Response также предоставляет методы:

  • clone() - клонирование объекта Response.
  • error() - возвращает новый объект Response содержащий ошибку.
  • redirect() - создает новый ответ с другим адресом.
  • arrayBuffer() - возвращает промис содержащий ArrayBuffer.
  • blob() - возвращает промис содержащий Blob.
  • formData() - возвращает промис содержащий объект FormData.
  • json() - возвращает промис содержащий JSON.
  • text() - возвращает промис содержащий USVString (текст).

Работа с JSON

Пример запроса, возвращающего JSON:

fetch('http://test.com/ajax-endpoint').then(function(response) { 
    // Конвертация ответа в JSON
    return response.json();
}).then(function(j) {
    // А здесь уже объект JavaScript
    console.log(j); 
});

Обработка Basic Text/HTML ответов

JSON - это самый желанный формат данных, но часто приходится работать с HTML или текстом:

fetch('/next/page')
  .then(function(response) {
    return response.text();
  }).then(function(text) { 
    // <!DOCTYPE ....
    console.log(text); 
  });

Обработка Blob ответов

Если хочется например загрузить изображение через fetch, то:

fetch('http://test.com/flowers.jpg')
    .then(function(response) {
      return response.blob();
    })
    .then(function(imageBlob) {
      document.querySelector('img').src = URL.createObjectURL(imageBlob);
    });

Отправка данных с Form Data

Еще один частый пример использования AJAX - отправка данных из формы:

fetch('http://test.com/ajax-endpoint', {
    method: 'post',
    body: new FormData(document.getElementById('comment-form'))
});

Ну а если надо отправить JSON:

fetch('http://test.com/ajax-endpoint', {
    method: 'post',
    body: JSON.stringify({
        email: document.getElementById('email').value,
        answer: document.getElementById('answer').value
    })
});

Очень просто и очень удобно!