Пошук уроків, статей та іншого контенту
Навчитеся читати тіло відповіді як JSON, текст, Blob або ArrayBuffer та обирати відповідний метод.
Після виконання fetch() ми отримуємо об’єкт Response. Він містить статус, заголовки та тіло HTTP-відповіді.
const response = await fetch("/api/users");
console.log(response.status); // Наприклад, 200
console.log(response.headers); // Заголовки відповідіТіло відповіді не читається автоматично. Для цього потрібно викликати один із методів:
response.json() — прочитати тіло як JSON;
response.text() — прочитати тіло як текст;
response.blob() — отримати Blob, наприклад файл або зображення;
response.arrayBuffer() — отримати необроблені бінарні дані.
Усі ці методи є асинхронними та повертають Promise.
Метод json() читає тіло відповіді та намагається перетворити його на JavaScript-значення за допомогою JSON-парсера.
async function loadUser() {
const response = await fetch("/api/user/42");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const user = await response.json();
console.log(user.name);
console.log(user.email);
}
loadUser().catch((error) => {
console.error("Не вдалося завантажити користувача:", error);
});Якщо сервер повернув, наприклад:
{
"name": "Олена",
"email": "olena@example.com"
}то змінна user буде звичайним JavaScript-об’єктом.
Метод json() підходить для відповідей API, коли сервер повертає структуровані дані:
об’єкти;
масиви;
рядки, числа або булеві значення у форматі JSON.
Статус HTTP та коректність JSON — це різні речі. Сервер може повернути статус 200, але тіло відповіді може бути невалідним JSON.
async function loadSettings() {
const response = await fetch("/api/settings");
if (!response.ok) {
throw new Error(`Сервер повернув ${response.status}`);
}
try {
return await response.json();
} catch {
throw new Error("Сервер повернув некоректний JSON");
}
}Також помилка виникне, якщо тіло порожнє. Наприклад, виклик response.json() для відповіді без тіла не поверне null автоматично — парсер не матиме що читати.
Метод text() повертає тіло як рядок.
async function loadMessage() {
const response = await fetch("/message.txt");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const message = await response.text();
document.querySelector("#message").textContent = message;
}
loadMessage().catch((error) => {
console.error(error);
});text() підходить для:
звичайних текстових файлів;
HTML;
XML;
CSV;
SVG у текстовому вигляді;
відповідей API, які не мають JSON-формату.
Якщо відповідь містить HTML і ви хочете показати його як звичайний текст, використовуйте textContent, а не innerHTML:
const response = await fetch("/fragment.html");
const html = await response.text();
document.querySelector("#preview").textContent = html;Так безпечніше, якщо вміст надходить із ненадійного джерела.
Blob — це об’єкт, який представляє необроблені дані, наприклад зображення, PDF-документ або аудіофайл.
Метод blob() зручний, коли потрібно передати отримані дані браузерному API.
async function showAvatar() {
const response = await fetch("/images/avatar.png");
if (!response.ok) {
throw new Error(`Не вдалося завантажити зображення: ${response.status}`);
}
const imageBlob = await response.blob();
const imageUrl = URL.createObjectURL(imageBlob);
const image = document.querySelector("#avatar");
image.src = imageUrl;
image.addEventListener("load", () => {
// Після завантаження зображення тимчасову URL-адресу можна звільнити
URL.revokeObjectURL(imageUrl);
});
}
showAvatar().catch((error) => {
console.error(error);
});Приклад HTML для цього коду:
<img id="avatar" alt="Аватар користувача">URL.createObjectURL() створює тимчасову URL-адресу для Blob. Після завершення роботи її бажано звільнити через URL.revokeObjectURL().
Той самий підхід можна використати для завантаження PDF або іншого файла:
async function downloadReport() {
const response = await fetch("/reports/monthly.pdf");
if (!response.ok) {
throw new Error(`Не вдалося завантажити звіт: ${response.status}`);
}
const reportBlob = await response.blob();
const reportUrl = URL.createObjectURL(reportBlob);
const link = document.createElement("a");
link.href = reportUrl;
link.download = "monthly-report.pdf";
link.click();
URL.revokeObjectURL(reportUrl);
}
downloadReport().catch((error) => {
console.error("Помилка завантаження:", error);
});arrayBuffer() повертає дані як ArrayBuffer — ділянку пам’яті з бінарними даними.
async function loadBinaryData() {
const response = await fetch("/files/data.bin");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const buffer = await response.arrayBuffer();
console.log("Кількість байтів:", buffer.byteLength);
const bytes = new Uint8Array(buffer);
console.log("Перші байти:", bytes.slice(0, 8));
}
loadBinaryData().catch((error) => {
console.error(error);
});ArrayBuffer доцільно використовувати, коли потрібно самостійно аналізувати байти або передати їх до API, яке працює з бінарними даними:
TypedArray, наприклад Uint8Array;
DataView;
Web Audio API;
криптографічні операції;
обробка спеціальних бінарних форматів.
На відміну від Blob, ArrayBuffer зручніший для низькорівневої роботи з окремими байтами.
Методи читання тіла мають різні результати:
const jsonData = await response.json(); // Об'єкт або масив
const textData = await response.text(); // Рядок
const fileData = await response.blob(); // Blob
const binaryData = await response.arrayBuffer(); // ArrayBufferВибір залежить не від назви URL, а від фактичного формату відповіді:
API повертає JSON — json();
сервер повертає текст або HTML — text();
потрібен файл для показу чи завантаження — blob();
потрібно працювати з байтами — arrayBuffer().
Заголовок Content-Type може підказати формат:
const contentType = response.headers.get("content-type");
console.log(contentType);
// Наприклад: "application/json; charset=utf-8"Але заголовок не завжди правильно налаштований, тому він не гарантує, що тіло справді має відповідний формат.
Тіло Response є потоком. Після того як один із методів читання завершився, тіло вважається використаним.
const response = await fetch("/api/user");
const user = await response.json();
// Помилка: тіло вже було прочитане
const text = await response.text();Після першого читання властивість bodyUsed матиме значення true:
const response = await fetch("/api/user");
console.log(response.bodyUsed); // false
await response.json();
console.log(response.bodyUsed); // trueНе можна послідовно викликати json(), text(), blob() або arrayBuffer() для того самого об’єкта Response.
Якщо потрібно створити копію відповіді до читання тіла, використовуйте clone():
async function readResponseTwice() {
const response = await fetch("/api/data");
const copy = response.clone();
const data = await response.json();
const rawText = await copy.text();
console.log("Розібрані дані:", data);
console.log("Початковий текст:", rawText);
}
readResponseTwice().catch((error) => {
console.error(error);
});Копію потрібно створити до першого виклику методу читання.
У деяких випадках клієнт не знає наперед, який формат поверне сервер. Тоді можна перевірити Content-Type.
async function readResponse(response) {
const contentType = response.headers.get("content-type") ?? "";
if (contentType.includes("application/json")) {
return await response.json();
}
if (contentType.startsWith("text/")) {
return await response.text();
}
return await response.blob();
}
async function loadResource() {
const response = await fetch("/api/resource");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const data = await readResponse(response);
if (data instanceof Blob) {
console.log("Отримано бінарний файл:", data.type);
} else {
console.log("Отримані дані:", data);
}
}
loadResource().catch((error) => {
console.error("Не вдалося прочитати відповідь:", error);
});Такий підхід корисний для універсальних клієнтів, але для конкретного API краще заздалегідь знати очікуваний формат і явно викликати потрібний метод.
fetch() не відхиляє Promise лише через статус 404 або 500. У такій ситуації Promise зазвичай успішно повертає Response.
Тому статус потрібно перевіряти окремо:
async function requestJson(url) {
const response = await fetch(url);
if (!response.ok) {
let details = "";
try {
details = await response.text();
} catch {
// Тіло помилки може бути недоступним
}
throw new Error(
`HTTP ${response.status}: ${details || "без опису помилки"}`
);
}
return response.json();
}Помилка мережі, наприклад відсутність з’єднання або блокування запиту політикою браузера, призводить до відхилення Promise самого fetch().
HTTP-помилка обробляється перевіркою response.ok або response.status.
Нижче наведено сторінку, яка завантажує JSON, текст і зображення різними методами.
<!doctype html>
<html lang="uk">
<head>
<meta charset="UTF-8">
<title>Читання HTTP-відповідей</title>
</head>
<body>
<button id="load-user">Завантажити користувача</button>
<button id="load-text">Завантажити текст</button>
<button id="load-image">Завантажити зображення</button>
<pre id="output"></pre>
<img id="image" alt="Завантажене зображення" width="200">
<script>
const output = document.querySelector("#output");
const image = document.querySelector("#image");
async function loadUser() {
const response = await fetch("/api/user");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const user = await response.json();
output.textContent = JSON.stringify(user, null, 2);
}
async function loadText() {
const response = await fetch("/documents/readme.txt");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const text = await response.text();
output.textContent = text;
}
async function loadImage() {
const response = await fetch("/images/photo.jpg");
if (!response.ok) {
throw new Error(`HTTP-помилка: ${response.status}`);
}
const blob = await response.blob();
const imageUrl = URL.createObjectURL(blob);
image.src = imageUrl;
image.addEventListener("load", () => {
// Звільняємо тимчасову URL-адресу після завантаження
URL.revokeObjectURL(imageUrl);
}, { once: true });
}
document.querySelector("#load-user").addEventListener("click", () => {
loadUser().catch(showError);
});
document.querySelector("#load-text").addEventListener("click", () => {
loadText().catch(showError);
});
document.querySelector("#load-image").addEventListener("click", () => {
loadImage().catch(showError);
});
function showError(error) {
output.textContent = `Помилка: ${error.message}`;
}
</script>
</body>
</html>Для запуску прикладу сервер має мати відповідні ресурси:
/api/user із JSON-відповіддю;
/documents/readme.txt із текстом;
/images/photo.jpg із зображенням.
json() без awaitconst response = await fetch("/api/user");
const user = response.json();
console.log(user.name); // undefinedresponse.json() повертає Promise. Потрібно дочекатися результату:
const user = await response.json();
console.log(user.name);Або використати .then():
fetch("/api/user")
.then((response) => response.json())
.then((user) => {
console.log(user.name);
});const response = await fetch("/api/data");
const data = await response.json();
const text = await response.text(); // ПомилкаЯкщо потрібні два представлення тіла, спочатку створіть копію через response.clone().
404 автоматично викличе catchtry {
const response = await fetch("/missing-resource");
console.log(response.status);
} catch {
console.log("Цей блок не обов'язково виконається для 404");
}Потрібно явно перевіряти response.ok:
const response = await fetch("/missing-resource");
if (!response.ok) {
throw new Error(`Ресурс не знайдено: ${response.status}`);
}json() для текстової відповідіЯкщо сервер повертає HTML або звичайний текст, json() завершиться помилкою синтаксичного аналізу. Для такого тіла потрібно використовувати text().
Після URL.createObjectURL() браузер утримує створену тимчасову URL-адресу. Коли вона більше не потрібна, її слід звільнити:
URL.revokeObjectURL(objectUrl);innerHTML для ненадійного текстуЯкщо відповідь містить неперевірений текст, вставляйте його через textContent. innerHTML може виконати небезпечну HTML-розмітку.
fetch() повертає об’єкт Response, а тіло потрібно прочитати окремо.
json() перетворює JSON-відповідь на JavaScript-значення.
text() повертає тіло як рядок.
blob() підходить для файлів, зображень та інших даних, які потрібно передати браузерним API.
arrayBuffer() використовується для низькорівневої роботи з бінарними даними.
Усі методи читання тіла є асинхронними.
Тіло однієї відповіді можна прочитати лише один раз.
Для повторного читання потрібно заздалегідь викликати response.clone().
Статус 404 або 500 не є автоматичною помилкою fetch(), тому перевіряйте response.ok.
Обирайте метод читання відповідно до формату даних, які повертає сервер.