Fetch API

С возвращением.
В предыдущем уроке ты познакомился с local storage.
Local storage позволяет JavaScript запоминать маленькие кусочки данных в браузере.
Очень полезно.
Очень практично.
Очень в стиле: “у браузера теперь есть маленький блокнот.”
Сегодня мы научим JavaScript получать данные извне страницы.
И тут всё становится очень реальным.
Веб-сайты редко держат все данные в одном JavaScript-файле.
Они часто загружают данные из:
- API;
- серверов;
- JSON-файлов;
- баз данных через backend endpoints;
- других сервисов.
Чтобы получить эти данные, JavaScript использует Fetch API.
Fetch позволяет JavaScript попросить данные.
Браузер говорит:
Я пойду и принесу.
JavaScript отвечает:
Хорошо. Только, пожалуйста, не возвращайся с хаосом.
Иногда он возвращается с данными.
Иногда — с ошибкой.
Иногда — с чем-то, что заставляет тебя переосмыслить жизненные решения.
Добро пожаловать в веб-разработку.
Что Ты Изучишь
В этом уроке ты изучишь:
- что такое Fetch API;
- что такое JSON;
- почему
fetch()асинхронный; - как загрузить локальный JSON-файл;
- как использовать
asyncиawait; - как преобразовать response в JSON;
- как показывать loading-сообщения;
- как обрабатывать ошибки через
tryиcatch; - как показывать загруженные данные в DOM;
- как создать маленький список товаров.
В конце этого урока твоя страница будет загружать данные из JSON-файла и показывать их на экране.
Это огромный шаг.
До этого большинство данных жило прямо внутри JavaScript.
Теперь JavaScript начнёт брать данные снаружи.
Как frontend-посланник.
В хорошей обуви.
Что Такое Fetch?
fetch() — это JavaScript-функция для запроса данных.
Пример:
fetch("products.json");
Это просит браузер загрузить файл products.json.
Но есть важный момент.
fetch() не возвращает данные мгновенно.
Это занимает время.
Даже если файл маленький.
Даже если файл локальный.
Даже если ты просишь очень вежливо.
Поэтому JavaScript воспринимает fetch() как асинхронную операцию.
Это означает:
Начни запрос сейчас.
Продолжай, когда результат будет готов.
Это важно.
Очень важно.
Интернет не мгновенный.
Даже когда притворяется таким.
Что Такое JSON?
JSON означает JavaScript Object Notation.
Это текстовый формат для хранения и передачи данных.
Он похож на JavaScript-объекты.
Пример JSON:
{
"name": "JavaScript Course",
"price": 49,
"available": true
}
JSON повсюду.
API используют JSON.
Backend-ы возвращают JSON.
Frontend-приложения читают JSON.
Твой будущий backend на Java Spring Boot, скорее всего, тоже будет отдавать JSON.
А frontend будет пить его как кофе.
Осторожно.
Потому что плохой JSON приносит грусть.
Создай Проект
Создай папку для этого урока:
mkdir javascript-lesson11
cd javascript-lesson11
touch index.html
touch script.js
touch products.json
Твой проект должен выглядеть так:
javascript-lesson11/
index.html
script.js
products.json
Важно:
В этом уроке не открывай index.html напрямую двойным кликом.
Потому что fetch() может неправильно загружать локальные файлы через file://.
Используй локальный сервер.
Например, с Caddy:
caddy file-server --listen :8080
Потом открой:
http://localhost:8080
Теперь браузер сможет нормально загрузить JSON-файл.
Без framework-дракона.
Просто маленький локальный сервер.
Цивилизованно.
Напиши JSON-Файл
Открой products.json и добавь:
[
{
"id": 1,
"name": "JavaScript Course",
"price": 49,
"available": true
},
{
"id": 2,
"name": "HTML Course",
"price": 29,
"available": true
},
{
"id": 3,
"name": "CSS Course",
"price": 39,
"available": false
}
]
Это массив объектов.
Каждый объект представляет один товар.
Каждый товар имеет:
id;name;price;available.
Это очень распространено.
Список товаров?
Массив объектов.
Список пользователей?
Массив объектов.
Список постов блога?
Массив объектов.
JavaScript видит массивы объектов повсюду.
Как голубей на площади.
Напиши HTML
Открой index.html и добавь:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fetch API</title>
</head>
<body>
<h1>Fetch API</h1>
<button id="loadButton">Загрузить Товары</button>
<p id="message">Нажми кнопку, чтобы загрузить товары.</p>
<ul id="productList"></ul>
<script src="script.js"></script>
</body>
</html>
У нас есть:
- кнопка;
- место для сообщения;
- список для товаров.
Просто.
Чисто.
Готово получать данные.
Страница ждёт.
Как официант с пустым подносом.
Твой Первый Fetch
Открой script.js и добавь:
const loadButton = document.getElementById("loadButton");
const messageElement = document.getElementById("message");
const productListElement = document.getElementById("productList");
async function loadProducts() {
const response = await fetch("products.json");
const products = await response.json();
console.log(products);
}
loadButton.addEventListener("click", loadProducts);
Запусти локальный сервер:
caddy file-server --listen :8080
Открой:
http://localhost:8080
Нажми кнопку.
Открой консоль.
Ты должен увидеть массив товаров.
Поздравляю.
JavaScript загрузил данные из JSON-файла.
Это реально.
Маленькое реально.
Но реально.
Понимание async и await
Посмотри на эту функцию:
async function loadProducts() {
const response = await fetch("products.json");
const products = await response.json();
console.log(products);
}
Слово async означает:
Эта функция выполняет асинхронную работу.
Слово await означает:
Подожди, пока эта операция завершится, перед тем как двигаться дальше.
Эта строка:
const response = await fetch("products.json");
ждёт, пока файл загрузится.
Эта строка:
const products = await response.json();
ждёт, пока response преобразуется в JavaScript-данные.
Без await JavaScript не ждёт.
А потом код становится растерянным.
Как пытаться есть пасту до того, как она сварилась.
Технически возможно.
Эмоционально неправильно.
Что Такое response?
fetch() даёт нам объект response.
Пример:
const response = await fetch("products.json");
Response содержит информацию о запросе.
Это ещё не финальные данные.
Чтобы получить JSON-данные, мы используем:
const products = await response.json();
Это преобразует тело response в JavaScript-данные.
Важно:
response.json() тоже асинхронный.
Поэтому мы используем await.
JavaScript нужен момент, чтобы прочитать и распарсить JSON.
Даже JavaScript нуждается в минутке.
Уважай процесс.
Показ Товаров на Странице
Теперь покажем товары в DOM.
Обнови script.js:
const loadButton = document.getElementById("loadButton");
const messageElement = document.getElementById("message");
const productListElement = document.getElementById("productList");
async function loadProducts() {
const response = await fetch("products.json");
const products = await response.json();
productListElement.innerHTML = "";
for (const product of products) {
const listItem = document.createElement("li");
listItem.textContent = `${product.name} - €${product.price}`;
productListElement.appendChild(listItem);
}
messageElement.textContent = `Загружено товаров: ${products.length}.`;
}
loadButton.addEventListener("click", loadProducts);
Обнови браузер.
Нажми кнопку.
Теперь товары появляются на странице.
Что произошло?
JavaScript:
- загрузил
products.json; - преобразовал его в JavaScript-данные;
- прошёлся по товарам циклом;
- создал элементы
<li>; - добавил их на страницу.
Это frontend development.
Данные приходят.
DOM обновляется.
Пользователь видит контент.
Красиво.
Немного драматично.
Но красиво.
Добавь Статус Доступности
Теперь покажем, доступен ли товар.
Замени эту строку:
listItem.textContent = `${product.name} - €${product.price}`;
на эту:
const status = product.available ? "Доступен" : "Недоступен";
listItem.textContent = `${product.name} - €${product.price} - ${status}`;
Полный цикл:
for (const product of products) {
const listItem = document.createElement("li");
const status = product.available ? "Доступен" : "Недоступен";
listItem.textContent = `${product.name} - €${product.price} - ${status}`;
productListElement.appendChild(listItem);
}
Это использует ternary operator.
Он означает:
Если product.available true, используй "Доступен".
Иначе используй "Недоступен".
Коротко.
Полезно.
В начале немного подозрительно.
Но после практики дружелюбно.
Добавь Loading State
Загрузка данных занимает время.
Поэтому стоит показать сообщение о загрузке.
Обнови loadProducts:
async function loadProducts() {
messageElement.textContent = "Загрузка товаров...";
productListElement.innerHTML = "";
const response = await fetch("products.json");
const products = await response.json();
for (const product of products) {
const listItem = document.createElement("li");
const status = product.available ? "Доступен" : "Недоступен";
listItem.textContent = `${product.name} - €${product.price} - ${status}`;
productListElement.appendChild(listItem);
}
messageElement.textContent = `Загружено товаров: ${products.length}.`;
}
Теперь после клика страница говорит:
Загрузка товаров...
Это хороший user experience.
Пользователи любят знать, что что-то происходит.
Иначе они кликают снова.
И снова.
И снова.
Потом твоё приложение получает 47 запросов и начинает плакать.
Обработка Ошибок через try и catch
Иногда fetch не удаётся.
Возможно, неправильное имя файла.
Возможно, сервер не запущен.
Возможно, интернет переживает философский кризис.
Нам нужно обрабатывать ошибки.
Обнови script.js:
const loadButton = document.getElementById("loadButton");
const messageElement = document.getElementById("message");
const productListElement = document.getElementById("productList");
async function loadProducts() {
messageElement.textContent = "Загрузка товаров...";
productListElement.innerHTML = "";
try {
const response = await fetch("products.json");
const products = await response.json();
for (const product of products) {
const listItem = document.createElement("li");
const status = product.available ? "Доступен" : "Недоступен";
listItem.textContent = `${product.name} - €${product.price} - ${status}`;
productListElement.appendChild(listItem);
}
messageElement.textContent = `Загружено товаров: ${products.length}.`;
} catch (error) {
messageElement.textContent = "Не удалось загрузить товары.";
console.log(error);
}
}
loadButton.addEventListener("click", loadProducts);
Теперь ошибки не разрушают атмосферу молча.
Если что-то пойдёт не так, пользователь увидит сообщение.
А разработчик увидит ошибку в консоли.
Все получают информацию.
Очень здорово.
Очень редко.
Проверка response.ok
Есть ещё один важный момент.
fetch() не всегда бросает ошибку при плохих HTTP-ответах.
Например, если файл не существует, сервер может вернуть 404.
Поэтому стоит проверить:
if (!response.ok) {
throw new Error("Не удалось загрузить товары.");
}
Обнови часть с fetch:
const response = await fetch("products.json");
if (!response.ok) {
throw new Error("Не удалось загрузить товары.");
}
const products = await response.json();
Полная функция:
async function loadProducts() {
messageElement.textContent = "Загрузка товаров...";
productListElement.innerHTML = "";
try {
const response = await fetch("products.json");
if (!response.ok) {
throw new Error("Не удалось загрузить товары.");
}
const products = await response.json();
for (const product of products) {
const listItem = document.createElement("li");
const status = product.available ? "Доступен" : "Недоступен";
listItem.textContent = `${product.name} - €${product.price} - ${status}`;
productListElement.appendChild(listItem);
}
messageElement.textContent = `Загружено товаров: ${products.length}.`;
} catch (error) {
messageElement.textContent = "Не удалось загрузить товары.";
console.log(error);
}
}
Теперь код сильнее.
Не бессмертный.
Но сильнее.
Как кофе после второй чашки.
Добавь Лучшие Стили
Сделаем страницу красивее.
Обнови index.html:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fetch API</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 60px auto;
padding: 0 24px;
background-color: #f3f4f6;
color: #111827;
}
.card {
background-color: white;
padding: 24px;
border: 2px solid #e5e7eb;
border-radius: 18px;
}
h1 {
font-size: 42px;
}
button {
background-color: #2563eb;
color: white;
border: none;
padding: 14px 20px;
border-radius: 999px;
font-weight: 700;
cursor: pointer;
}
button:hover {
background-color: #1d4ed8;
}
.message {
margin-top: 20px;
font-size: 18px;
font-weight: 700;
}
.products {
list-style: none;
padding: 0;
margin-top: 20px;
display: grid;
gap: 12px;
}
.product {
padding: 16px;
border: 2px solid #e5e7eb;
border-radius: 14px;
background-color: #f9fafb;
}
.product strong {
display: block;
font-size: 22px;
margin-bottom: 6px;
}
.available {
color: #166534;
font-weight: 700;
}
.unavailable {
color: #991b1b;
font-weight: 700;
}
</style>
</head>
<body>
<h1>Fetch API</h1>
<div class="card">
<button id="loadButton">Загрузить Товары</button>
<p id="message" class="message">Нажми кнопку, чтобы загрузить товары.</p>
<ul id="productList" class="products"></ul>
</div>
<script src="script.js"></script>
</body>
</html>
Теперь обнови показ товаров в script.js:
for (const product of products) {
const listItem = document.createElement("li");
const status = product.available ? "Доступен" : "Недоступен";
const statusClass = product.available ? "available" : "unavailable";
listItem.className = "product";
listItem.innerHTML = `
<strong>${product.name}</strong>
<span>Цена: €${product.price}</span><br>
<span class="${statusClass}">${status}</span>
`;
productListElement.appendChild(listItem);
}
Теперь товары выглядят как карточки.
Намного лучше.
Меньше “сырой HTML из пещеры.”
Больше “маленькое frontend-приложение для начинающих.”
Прогресс.
Полный JavaScript Код
Вот полный script.js:
const loadButton = document.getElementById("loadButton");
const messageElement = document.getElementById("message");
const productListElement = document.getElementById("productList");
async function loadProducts() {
messageElement.textContent = "Загрузка товаров...";
productListElement.innerHTML = "";
try {
const response = await fetch("products.json");
if (!response.ok) {
throw new Error("Не удалось загрузить товары.");
}
const products = await response.json();
for (const product of products) {
const listItem = document.createElement("li");
const status = product.available ? "Доступен" : "Недоступен";
const statusClass = product.available ? "available" : "unavailable";
listItem.className = "product";
listItem.innerHTML = `
<strong>${product.name}</strong>
<span>Цена: €${product.price}</span><br>
<span class="${statusClass}">${status}</span>
`;
productListElement.appendChild(listItem);
}
messageElement.textContent = `Загружено товаров: ${products.length}.`;
} catch (error) {
messageElement.textContent = "Не удалось загрузить товары.";
console.log(error);
}
}
loadButton.addEventListener("click", loadProducts);
Теперь у тебя есть:
- JSON-файл;
- страница;
- fetch-запрос;
- loading state;
- обработка ошибок;
- рендеринг в DOM.
Это уже серьёзный beginner-проект.
Маленький.
Но серьёзный.
Как маленький espresso.
Типичные Ошибки
Открывать index.html Напрямую
Неправильно:
file:///home/user/javascript-lesson11/index.html
Лучше:
caddy file-server --listen :8080
Потом открой:
http://localhost:8080
Fetch часто требует сервер.
Даже маленький локальный.
Не дерись с браузером.
У него правила.
Много правил.
Забыть await
Неправильно:
const response = fetch("products.json");
const products = response.json();
Правильно:
const response = await fetch("products.json");
const products = await response.json();
Без await у тебя ещё нет данных.
У тебя есть promise.
Promise — это не результат.
Это будущий результат.
Как заказ пиццы.
Чек — это не пицца.
Снова пицца.
Но работает.
Забыть response.json()
Неправильно:
const products = await response;
Правильно:
const products = await response.json();
Response — это не финальные данные.
Нужно преобразовать его в JSON.
JavaScript мощный.
Но ему всё равно нужны инструкции.
Неправильный Путь к Файлу
Если файл называется:
products.json
тогда fetch:
fetch("products.json")
Если файл в папке:
data/products.json
тогда fetch:
fetch("data/products.json")
Пути должны совпадать.
JavaScript не будет искать по папкам как детектив.
Он просто упадёт.
Тихо.
Как кот, который скидывает что-то со стола.
Практика
Создай новый JSON-файл с названием students.json.
Добавь это:
[
{
"name": "Anna",
"level": "Beginner"
},
{
"name": "Marco",
"level": "Intermediate"
},
{
"name": "Viktor",
"level": "Beginner"
}
]
Создай кнопку, которая загружает студентов.
Покажи каждого студента на странице.
Пример результата:
Anna - Beginner
Marco - Intermediate
Viktor - Beginner
Бонус:
Покажи, сколько студентов было загружено.
Очень похоже на товары.
Другие данные.
Та же схема.
Так работает программирование.
Выучи один шаблон.
Используй его везде.
Делай вид, что было сложно.
Очень профессионально.
Мини-Челлендж
Создай маленький каталог курсов.
Создай courses.json с:
- названием курса;
- категорией;
- ценой;
- статусом доступности.
Потом используй fetch(), чтобы загрузить курсы и показать их как карточки.
Каждая карточка должна показывать:
- название;
- категорию;
- цену;
- доступность.
Бонус:
Показывай только доступные курсы.
Это означает, что можно использовать:
if (course.available) {
// show course
}
Этот челлендж соединяет:
- массивы;
- объекты;
- DOM;
- условия;
- fetch;
- JSON.
Короче говоря, JavaScript становится маленькой фабрикой веб-приложений.
Поздравляю.
И ещё: осторожно.
Фабрикам нужна организация.
Итог
Сегодня ты изучил:
fetch()загружает данные из файлов или API;- fetch асинхронный;
asyncпозволяет функции использоватьawait;awaitждёт асинхронные результаты;- JSON — популярный формат данных;
response.json()преобразует response в JavaScript-данные;- loading-сообщения улучшают user experience;
tryиcatchобрабатывают ошибки;response.okпомогает выявлять неудачные запросы;- загруженные данные можно показывать в DOM;
- локальные серверы помогают избежать проблем с локальными файлами.
Это огромный шаг.
Твой JavaScript теперь может говорить с внешними данными.
Не только с переменными.
Не только с local storage.
Настоящие файлы.
Потом API.
Потом серверы.
Внешний мир.
JavaScript открыл окно.
Надеюсь, не неправильное.
Следующий Урок
В следующем уроке мы создадим маленький финальный проект.
Мы объединим:
- переменные;
- массивы;
- объекты;
- функции;
- DOM;
- события;
- формы;
- валидацию;
- local storage;
- fetch.
Настоящий JavaScript-проект для начинающих.
Не огромный.
Не страшный.
Но полный.
Final boss приближается.
Принеси кофе.