JavaScript Fetch API
В этой статье познакомимся с API-интерфейсом JavaScript Fetch и узнаем о том, как его использовать для выполнения асинхронных HTTP-запросов.
Что такое Fetch API?
Fetch API – это простой и современный интерфейс, который позволяет выполнять сетевые запросы и обрабатывать ответы от сервера. Т.е. решает те же задачи, что XMLHttpRequest (XHR)
Самым большим отличием Fetch
от XMLHttpRequest
является то, что первый использует промисы, которые значительно упрощают работу с запросами и ответами. Код на Fetch
получается более простым и чистым.
Начиная с ES7, вы можете использовать async-await и полностью избавиться от обещаний.
Fetch API предоставляет глобальный метод fetch():
// url – Переменная, содержащая URL для отправки запроса
// options – различные настройки: метод, заголовки, режим и т.д.
let request = fetch(url, [options])
Отправка запроса и чтение ответа
Если не указывать метод, то Fetch API по умолчанию делает GET-запрос.
// url – переменная, содержащая URL
let request = fetch(url);
Метод fetch()
возвращает promise. Для обработки результата можно использовать методы then()
и catch()
:
let request = fetch('/examples/ajax/01.html');
request
.then(response => {
// обработка ответа
console.log( response );
})
.catch(error => {
// обработка ошибки
console.log( error );
});
При успешном выполнении запроса, мы получим объект Response
. У Response
есть ряд полезных свойств для проверки состояния ответа:
status
– код статуса;statusText
– текст статуса;ok
–true
, когда код статуса от 200 до 299;redirected
–true
, если при вызове запрошенного URL-адреса произошёл редирект.
Проверить, выполнен ли запрос успешно, можно с помощью свойства ok
:
let request = fetch('/examples/ajax/01.html');
request
.then(response => {
// если код статуса ответа от 200 до 299
if (response.ok) {
// запрос был успешно выполнен сервером
}
})
.catch(error => {
// обработка ошибки
console.log( error );
});
Промис (request
) завершается успешно даже когда запрошенный URL не существует (код ответа 404) или он вызывает ошибку 500. Просто будут другие значения свойств: status
, ok
и т.д.
В метод catch
мы попадём только в том случае, когда fetch() вообще не может выполнить запрос, т.е. нет такого сайта или произошла потеря сетевого соединения.
Для получения тела ответа, у объекта Response имеются следующие методы:
text()
– как текст;json()
– в формате JSON;formData()
– как объект FormData;blob()
– в формате Blob;arrayBuffer()
– как ArrayBuffer.
Все эти методы возвращают promise, который в конечном итоге выполняется и выводит содержимое.
Например, прочитаем ответ как строку и выведем её в элемент с id="result"
:
<button type="button" id="get-text">Получить текст с сервера</button>
<div id="result"></div>
<script>
document.querySelector('#get-text').onclick = () => {
fetch('/examples/ajax/01.html')
.then(response => {
if (response.ok) {
response.text().then(data => {
// выведем данные в #result
document.querySelector('#result').innerHTML = data;
});
}
})
.catch(error => {
console.log(error);
});
}
</script>
Вместо обещаний можно использовать async-await:
<button type="button" id="get-text">Получить текст с сервера</button>
<div id="result"></div>
<script>
document.querySelector('#get-text').onclick = async () => {
try {
let response = await fetch('/examples/ajax/01.html');
if (response.ok) {
let data = await response.text();
// выведем данные в #result
document.querySelector('#result').innerHTML = data;
}
}
catch (error) {
console.log(error);
}
}
</script>
Пример использования fetch() для получения JSON
Напишем пример, в котором будем получать информацию о пользователях в формате JSON. Для того, чтобы запросить данные об конкретном пользователе будем брать значение из поля, а затем добавлять его в URL посредством GET-параметра id
.

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(`/examples/ajax/05.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) {
console.log(error);
}
}
// при клике на #get-user
document.querySelector('#get-user').onclick = () => {
const id = parseInt(document.querySelector('#user-id').value);
// вызовем функцию getUser и передадим ей id пользователя который нужно получить
getUsers(id);
}
// при клике на #get-users
document.querySelector('#get-users').onclick = () => {
// вызовем функцию getUser
getUsers();
}
</script>
Содержимое файла «05.php», который возвращает данные о пользователях в формате JSON:
<?php
$id = isset($_GET['id']) ? (int) ($_GET['id']) : -1;
$users = [
1 => [
'avatar_url' => '/examples/ajax/img-01.jpg',
'name' => 'Александр Мухин',
'email' => 'alexander@mail.ru',
'location' => 'Россия'
],
2 => [
'avatar_url' => '/examples/ajax/img-02.jpg',
'name' => 'Евгений Смирнов',
'email' => 'evgeniy@gmail.com',
'location' => 'Украина'
],
3 => [
'avatar_url' => '/examples/ajax/img-03.jpg',
'name' => 'Ольга Соколова',
'email' => 'olga@yandex.ru',
'location' => 'Россия'
]
];
$output = ['count' => 0, 'data' => []];
if ($id > 0 & $id <= count($users)) {
$output['count'] = 1;
$output['data'] = [$id => $users[$id]];
} elseif ($id == -1) {
$output['count'] = count($users);
$output['data'] = $users;
}
echo json_encode($output);
Пример простого запроса HTTP POST
<style>
.form-wrapper {
position: relative;
}
.result {
display: none;
}
.form-success .result {
display: flex;
}
</style>
<div class="form-wrapper">
<form action="/examples/ajax/06.php" method="post" id="user">
<div class="form-group">
<label for="name">Имя</label>
<input type="text" name="name" id="name" value="">
<div class="error"></div>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" name="email" id="email" value="">
<div class="error"></div>
</div>
<button type="submit">Отправить</button>
</form>
<div class="result">
<div class="result__text">Форма успешно отправлена!</div>
<div class="result_ok"></div>
<button type="button" class="result__close">Ok</button>
</div>
</div>
<script>
const sendUser = async () => {
try {
let response = await fetch(document.forms.user.action, {
method: 'post',
body: new FormData(document.forms.user)
});
if (response.ok) {
let result = await response.json();
document.forms.user.querySelectorAll('.error').forEach(el => {
el.textContent = '';
})
if (result['result'] === 'error') {
const errors = result['error'];
for (const [key, value] of Object.entries(errors)) {
document.forms.user.querySelector(`[name="${key}"]`).nextElementSibling.textContent = value;
}
} else {
document.forms.user.reset();
document.forms.user.closest('.form-wrapper').classList.add('form-success');
}
}
}
catch (error) {
console.log(error);
}
}
document.forms.user.onsubmit = (e) => {
e.preventDefault();
sendUser();
}
document.querySelector('.result__close').onclick = (e) => {
e.target.closest('.form-wrapper').classList.toggle('form-success');
}
</script>
Содержимое php-файла:
<?php
$result = ['result' => 'success', 'error' => []];
$name = '';
$email = '';
if (empty($_POST['name'])) {
$result['result'] = 'error';
$result['error']['name'] = 'Это поле не заполнено!';
} else {
$name = $_POST['name'];
if (!preg_match("/^[a-zа-яё\s]+$/iu", $name)) {
$result['result'] = 'error';
$result['error']['name'] = 'Имя содержит недопустимые символы!';
}
}
if (empty($_POST['email'])) {
$result['result'] = 'error';
$result['error']['email'] = 'Это поле не заполнено!';
} else {
$email = $_POST['email'];
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$result['result'] = 'error';
$result['error']['email'] = 'Поле email заполнено некорректно!';
}
}
$data = [];
if (file_exists('users.txt')) {
$data = json_decode(file_get_contents('users.txt'), true);
}
if (array_key_exists($email, $data)) {
$result['result'] = 'error';
$result['error']['email'] = 'Пользователь с таким email уже существует!';
} else {
$data[$email] = $name;
}
if ($result['result'] == 'success') {
if (file_put_contents('users.txt', json_encode($data), LOCK_EX) == false) {
$result['result'] = 'error';
}
}
echo json_encode($result);
Вторая часть - получение данных:
<div class="users">
<button type="button" id="get-users">Получить список пользователей</button>
<table>
<thead>
<tr>
<th>#</th>
<th>Имя</th>
<th>Email</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<script>
document.querySelector('#get-users').onclick = async () => {
try {
let response = await fetch('/examples/ajax/07.php');
if (response.ok) {
let result = await response.json();
const table = document.querySelector('.users tbody');
let html = '';
let i = 1;
for (const [key, value] of Object.entries(result)) {
html += `<tr><td>${i++}</td><td>${value}</td><td>${key}</td></tr>`;
}
table.innerHTML = html;
}
}
catch (error) {
console.log(error);
}
}
</script>
Содержимое php-файла:
<?php
$output = [];
if (file_exists('users.txt')) {
$output = json_decode(file_get_contents('users.txt'), true);
}
echo json_encode($output);
Добрый день, Александр.
Спасибо за познавательный и интересный контент. Подскажите пожалуйста, как правильно, в скрипте настроить запрос fetch, чтобы получить динамическое значение элемента (nodeValue) на удаленном сайте? Сайт, отдает данные в формате text/html и в результате преобразованияlet data = await response.text();
необходимых данных нет. Но в девтулзах, во вкладке Properties, эти значения отображаются. Спасибо.Добрый день! А что у вас в переменной data? Если там текст страницы, то используйте регулярку чтобы найти в нём нужное значение элемента.
Добрый день.
Спасибо, что ответили.Да, в переменной дата - текст страницы, но не полный.
Собственно, если подробнее о задаче.
Это тестовая страница, после перехода на нее, через рандомный промежуток времени, на странице запускается php-скрипт, который публикует в текстовом блоке, объект с тремя свойствами. По условию, нужно получить значение 3-го свойства и вывести его в JSON.https://dev.amidstyle.com/
Я еще только учусь и с юзкейсами такого плана не встречался, как их корректно решать не знаю.
Получается на момент действия моего скрипта, и обращения странице через fetch, указаный php-скрипт, еще не отрабатывает и в текстовом блоке, данные еще не опубликованы.
Возможно, я изначально не корректным способом пытаюсь решить данный тест?...Данный тест из области web-scrapinga, но интересно, решить ванильным JS, без использования библиотек для парсинга.
Спасибо, если сможете подсказать правильный метод или способ решения.С помощью fetch как только сервер отдаст ответ вы его сразу получите. Ничего дальше уже на сервере выполняться не будет. Если хотите получить полный ответ с помощью fetch, то отдавайте его только после того, когда он будет готов полностью.
Но вопрос не в том, чтобы прогресс сделать.
Есть задача отправить массив именно поэлементно (тяжелый массив c картинками в base64), дожидаясь окончания обработки каждого элемента php перед отправкой следующего элемента.
Вот и вопрос, как дожидаться ответа php скрипта, а потом снова выполнить тот же fetch
Отличная статья.
Столкнулся с проблемой при попытке сделать отображение прогресса обработки массива PHP обработчиком.
Например, имеем массив:
Вроде решение — которое лежит на поверхности — в цикле отправлять
элементы массива в JSON формате PHP обработчику, пока обработчик чего-то там делает — ждать ответа,
и в случае ответа про «все хорошо» — увеличить прогресс.
Однако, например Google Chrome говорит:
JavaScript console.log causes error: “Synchronous XMLHttpRequest on the main thread is deprecated…”
При чем раз-на-раз не приходится. Когда-то срабатывает, когда-то — выдает ошибку в консоль.
Если делать это асинхронными запросами, то (условно) прогресс стоит на месте до полной обработки
всех элементов и только потом разом стает 100%.
Подскажите. Есть-ли какой-то способ решить эту проблему?