Fetch API

Fetch API – это простой и современный интерфейс, который позволяет выполнять сетевые запросы и обрабатывать ответы от сервера. Т.е. решает те же задачи, что XMLHttpRequest (XHR).

Самым большим отличием Fetch от XMLHttpRequest является то, что первый использует промисы, которые значительно упрощают работу с запросами и ответами. Код на Fetch получается более простым и чистым.

Начиная с ES7, вы можете использовать async-await и полностью избавиться от promises.

метод fetch()

Fetch API предоставляет глобальный метод fetch():

let promise = fetch(url, [options]);
Возвращаемое значение

Метод Fetch возвращает Promise, содержащий объект Response (ответ на запрос).

С помощью свойств объекта Response можно получить из полученного ответа различную информацию. Объект Response имеет следующие свойства:

promise выполняется с объектом встроенного класса Response в качестве результата, как только сервер пришлёт заголовки ответа.

На этом этапе мы можем проверить статус HTTP-запроса и определить, выполнился ли он успешно, а также посмотреть заголовки, но пока без тела ответа.

Промис завершается с ошибкой, если fetch не смог выполнить HTTP-запрос, например при ошибке сети или если нет такого сайта. HTTP-статусы 404 и 500 не являются ошибкой.

Мы можем увидеть HTTP-статус в свойствах ответа:

Например:

let response = await fetch(url);

if (response.ok) { // если HTTP-статус в диапазоне 200-299
  // получаем тело ответа (см. про этот метод ниже)
  let json = await response.json();
} else {
  alert("Ошибка HTTP: " + response.status);
}

Для получения тела ответа нам нужно использовать дополнительный вызов метода.

Response предоставляет несколько методов, основанных на промисах, для доступа к телу ответа в различных форматах:

Например, получим JSON-объект с последними коммитами из репозитория на GitHub:

(async () => {
let url = 'xhr/periodic_table.json';
let response = await fetch(url);

let commits = await response.json(); // читаем ответ в формате JSON

alert(commits[0].elementName);
})()

То же самое без await, с использованием промисов:

fetch('xhr/periodic_table.json')
  .then(response => response.json())
  .then(commits => alert(commits[0].elementName));

Для получения ответа в виде текста используем await response.text() вместо .json():

(async () => {
let response = await fetch('xhr/periodic_table.json');

let text = await response.text(); // прочитать тело ответа как текст

alert(text.slice(0, 80) + '...');
})()

В качестве примера работы с бинарными данными, давайте запросим и выведем на экран логотип спецификации «fetch»:

(async () => {
let response = await fetch('image/logo-fetch.svg');

let blob = await response.blob(); // скачиваем как Blob-объект

// создаём <img>
let img = document.createElement('img');
img.style = 'position:fixed;top:30px;right:20px;width:192px';
document.body.append(img);

// выводим на экран
img.src = URL.createObjectURL(blob);

setTimeout(() => { // прячем через три секунды
  img.remove();
  URL.revokeObjectURL(img.src);
}, 3000);
})();

Мы можем выбрать только один метод чтения ответа.

Если мы уже получили ответ с response.text(), тогда response.json() не сработает, так как данные уже были обработаны.

let text = await response.text(); // тело ответа обработано
let parsed = await response.json(); // ошибка (данные уже были обработаны)

Комментарии

Типичный запрос с помощью fetch состоит из двух операторов await:

let response = await fetch(url, options); // завершается с заголовками ответа
let result = await response.json(); // читать тело ответа в формате JSON

Или, без await:

fetch(url, options)
  .then(response => response.json())
  .then(result => /* обрабатываем результат */)

Примеры

Отправка изображения

Мы можем отправить бинарные данные при помощи fetch, используя объекты Blob или BufferSource.

В этом примере есть элемент <canvas>, на котором мы можем рисовать движением мыши. При нажатии на кнопку «Отправить» изображение отправляется на сервер:

  <canvas id="canvasElem" width="192" height="192" style="border:1px solid"></canvas>
  <input type="button" value="Нарисуйте «мышкой» и Отправить" onclick="submit()">

  <script>
    canvasElem.onmousemove = function(e) {
      let ctx = canvasElem.getContext('2d');
      ctx.lineTo(e.clientX, e.clientY);
      ctx.stroke();
    };
    async function submit() {
      let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png'));
      let response = await fetch('xhr/f2.php', {
        method: 'POST',
        body: blob
      });

      // сервер ответит подтверждением и размером изображения
      let result = await response.text();
      alert(result);
    }
</script>

Пример использования fetch() для получения JSON

Получить информацию о пользователях в формате JSON.

HTML-код:
<input type="text" id="user-id" value="1">
<button type="button" id="get-user">Получить user по id</button>
<button type="button" id="get-users">Вывести всех</button>
<div id="result"></div>

<script>
  // функция для получения данных с сервера и вывода их на страницу
  const getUsers = async (id = -1) => {
    try {
      // использование метода fetch() для отправки асинхронного запроса на сервер
      let response = await fetch(`xhr/f3.php?id=${id}`);
      if (response.ok) {
        // получаем ответ в формате JSON и сохраняем его в data
        let data = await response.json();
        // выполняем рендеринг полученных данных в элемент #result
        const count = data['count'];
        let html = `<div style="margin-bottom: 10px;">Найдено: ${count}</div>`;
        for (const key in data['data']) {
          const user = data['data'][key];
          html += `<div style="background-color: #fafafa; display: flex; border-radius: 5px; overflow: hidden; margin-bottom: 10px;">
              <img src="${user['avatar_url']}" alt="${user['name']}">
              <ul>
                <li>Имя: ${user['name']}</li>
                <li>Email: ${user['email']}</li>
                <li>Страна: ${user['location']}</li>
              </ul>
            </div>`;
        }
        // выведем данные в #result
        document.querySelector('#result').innerHTML = html;
      }
    }
    catch (error) { сonsole.log(error); }
  }
  // при клике на #get-user
  document.querySelector('#get-user').onclick = () => {
    const id = parseInt(document.querySelector('#user-id').value);
    // вызовем функцию getUser и передадим ей id пользователя который нужно получить
    getUsers(id);
  }

  // при клике на #get-users вызовем функцию getUser
  document.querySelector('#get-users').onclick = () => { getUsers(); }
</script>

Далее для сравнения показаны примеры от XMLHttpRequest

Для прсмотра необходимо "кликнуть мышкой" на HTML или Результат

Пример 1

<script>
async function loadXMLDoc() { 
  var myDiv =  document.getElementById("myDiv");

  let response = await fetch("xhr/fetch_info.txt");

  if (response.ok) { // если HTTP-статус в диапазоне 200-299
     myDiv.innerHTML = await response.text(); }
  else {  myDiv.innerHTML = "Ошибка HTTP: " + response.status; } 
 }
</script>

<h2>Использование fetch</h2>
<div id="myDiv"></div>
<button type="button" onclick="loadXMLDoc()"> Попробуйте </button>



Пример 2

<script>
async function loadXMLDoc(file) { 
  var s='', myDiv =  document.getElementById("myDiv");

  let response = await fetch(file);

  if (response.ok) { // если HTTP-статус в диапазоне 200-299
     
     // перебрать все заголовки
    for (let [key, value] of response.headers)  s += `n${key} = ${value}<br>`;
     myDiv.innerHTML = s; }
  else {  myDiv.innerHTML = "Ошибка HTTP: " + response.status; } 
 }
</script>

<div id="myDiv"></div>
<button type="button" onclick="loadXMLDoc('xhr/fetch_info.txt')"> 
  Получить заголовочную информацию 
</button>



Пример 3

<script>
async function loadXMLDoc(file) { 
  var myDiv =  document.getElementById("myDiv");

  let response = await fetch(file);

  if (response.ok) { // если HTTP-статус в диапазоне 200-299
     myDiv.innerHTML = response.headers.get('Content-Type'); }
  else {  myDiv.innerHTML = "Ошибка HTTP: " + response.status; } 
 }
</script>

<div id="myDiv"></div>
<button type="button" onclick="loadXMLDoc('xhr/fetch_info.txt')"> 
  Получить информацию по заголовку "Content-Type" 
</button>



Пример 4

<script>
async function showCustomer(str) { 
  var myDiv =  document.getElementById("myDiv");

  let response = await fetch("xhr/ex4.php?q="+str);

  if (response.ok) {
     myDiv.innerHTML = await response.text(); }
  else {  myDiv.innerHTML = "Ошибка HTTP: " + response.status; } 
 }
</script>

<select onchange="showCustomer(this.value)">
<option value="">Изменить клиента:</option>
<option value="PETROVI">Иван Петров</option>
<option value="SIDOROVV">Василий Сидоров</option>
<option value="BYKOVA">Андрей Быков</option>
</select><br><br>

<div id="myDiv"> Информация о клиенте будет показана здесь...</div>



Пример 5

<script>
async function loadXMLDoc(url) { 
  var s, x, txt, xml, myDiv =  document.getElementById("myDiv");

  let response = await fetch(url);

  if (response.ok) {
     s = await response.text();
     xml = new window.DOMParser().parseFromString(s, "text/xml");
     txt="<table border='1'><tr>" + 
                   "<th>Заголовок</th>" +
                   "<th>Артист</th>"+
                   "</tr>";
     x = xml.documentElement.getElementsByTagName("CD");
     for (i=0;i<x.length;i++)
             { txt=txt + "<tr>"; xx=x[i].getElementsByTagName("TITLE");
               try { txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>"; }
               catch (er) { txt=txt + "<td> </td>"; }
               xx=x[i].getElementsByTagName("ARTIST");
               try { txt=txt + "<td>" + xx[0].firstChild.nodeValue + "</td>"; }
               catch (er) { txt=txt + "<td> </td>"; }
               txt=txt + "</tr>";
             }
      txt=txt + "</table>";
      myDiv.innerHTML=txt;
    }
  else {  myDiv.innerHTML = "Ошибка HTTP: " + response.status; } 
 }
</script>

<div id="myDiv"><h3> Получение содержимого XML файла </h3>
<button onclick="loadXMLDoc('xhr/cd_catalog.xml')"> 
  Получить информацию о CD дисках 
</button>
</div>



Пример 6

<script>
async function showHint(str) {
   var p1 = document.getElementById("p1");
   let response = await fetch("xhr/ex6.php?q="+encodeURIComponent(str));
   
  if (response.ok) {
     p1.innerHTML = await response.text(); }

  else { p1.innerHTML = "Ошибка HTTP: " + response.status; } 
 }
  
</script>

<h3>Начните печатать русскими буквами имя в поле ввода ниже:</h3>
Имя: <input type="text" id="txt1" onkeyup="showHint(this.value)" />
<p>Предложения: <span id="p1"></span></p> 



Пример 7

<style>
 li b {display: inline-block; width:32px;} 
 li span {display: inline-block; width:96px;}
</style>

<button onclick="loadTable()"> Загрузить periodic_table.json! </button>

<p id="p1"></p> 
<ul id="list"></ul>

<script>
async function loadTable() {
  let response = await fetch("xhr/periodic_table.json");

  if (!response.ok) { p1.innerHTML = "Ошибка HTTP: " + response.status; return;} 
  
  var table = await response.json();
  table.forEach(function(el) {
    var li = list.appendChild(document.createElement('li'));
    li.innerHTML = el.elementNumb + ': <b>' + el.elementChar + 
    '</b> <span>' + el.elementName + 
    '</span>    ' + decodeURIComponent(el.url);  });
 }
</script>



Пример 8

<form name="person">
<table>
 <tr><td style="text-align:right"> Имя: </td><td><input name="name" value="Никита"></td></tr>
 <tr><td style="text-align:right"> Фамилия: </td><td><input name="surname" value="Рябков"></td></tr>
</table>
</form>
<button onclick="sentForma()"> Отправить на сервер </button>
<div id="output"></div>
<script>
// создать объект для формы
var formData = new FormData(document.forms.person);

// добавить к пересылке ещё пару ключ - значение
formData.append("Age", "33");
formData.append("Profession", "Програмист");

async function sentForma () 
  { let response = await fetch('xhr/ex8.php', {
      method: 'POST',
      body: formData
    });

    document.getElementById("output").innerHTML = await response.text();
   }
</script>



Пример 9

// POST-запросы
var user = { name: "Tom", age: 23 };

(async () => { 
  let response = await fetch('xhr/postdata.php', {
      method: 'POST',
      headers: {'Content-Type': 'application/x-www-form-urlencoded'},
      body: "name=" + user.name + "&age="+user.age
    });
  document.getElementById("output").innerHTML = 
       "На Ваш запрос отвечаю:<br>" + await response.text();
  setTimeout(() => { // прячем через 5 секунды
      document.getElementById("output").innerHTML = "";
   }, 5000);
})();
Предполагается, что данные отправляются скрипту на языке php postdata.php, который может иметь, например, следующее содержание:
<?php
$name = "Не известно";
$age = "Не известно";
if(isset($_POST['name'])) $name = $_POST['name'];
if (isset($_POST['age'])) $age = $_POST['age'];
echo "Ваше имя: $name  <br> Ваш возраст: $age";
?>

Пример 10. Определить форму в html и использовать ее для отправки


<form name="user" action="xhr/postdata.php">
<input type="text" name="name" placeholder="Введите имя" /><br/>
<input type="text" name="age" placeholder="Введите возраст" /><br/>
<input type="submit" name="submit" value="Отправить" />
</form>
<p id="output"></p>
<script>
// получаем объект формы
var form = document.forms.user;
// прикрепляем обработчик кнопки
form.submit.addEventListener("click", sendRequest);
 
// обработчик нажатия
async function sendRequest(event) { event.preventDefault();
    var formData = new FormData(form);
    
    let response = await fetch(form.action, {
      method: 'POST',
      body: formData
    });
   
    if (response.ok) 
         document.getElementById("output").innerHTML =  await response.text();
  };
</script>
</body>
</html>