JavaScript · API · Продвинутый
Обработка ошибок
Надёжно обрабатывайте HTTP-ошибки и сбои сети с помощью response.ok и try/catch.
Краткое введение в тему и пояснения перед упражнениями (упражнения ниже):
Практические паттерны обработки ошибок
#Справочник обработки ошибок
#Упражнения:
Проверить response.ok
#Выполните запрос к `/api/products/99999/` (несуществующий). Выведите `response.status` и `response.ok`. Затем выполните запрос к `/api/products/1/` и сравните.
const BASE_URL = 'https://apilearn.tukas.dev';
async function main() {
// запрос к несуществующему товару — вывести status и ok
// запрос к реальному товару — вывести status и ok
}
main();
Решение
const BASE_URL = 'https://apilearn.tukas.dev';
async function main() {
const r1 = await fetch(`${BASE_URL}/api/products/99999/`);
console.log(r1.status, r1.ok); // 404 false
const r2 = await fetch(`${BASE_URL}/api/products/1/`);
console.log(r2.status, r2.ok); // 200 true
}
main();
Бросить исключение при HTTP-ошибке
#Напишите функцию `getProduct(id)`, которая получает товар и бросает `Error`, если `response.ok` равно `false`. Вызовите её с действительным id и с `99999`. Поймайте и выведите сообщение об ошибке.
const BASE_URL = 'https://apilearn.tukas.dev';
async function getProduct(id) {
// fetch, проверить response.ok, бросить исключение если не ok
}
async function main() {
// вызвать с действительным id
// вызвать с 99999 и поймать ошибку
}
main();
Решение
const BASE_URL = 'https://apilearn.tukas.dev';
async function getProduct(id) {
const response = await fetch(`${BASE_URL}/api/products/${id}/`);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
}
async function main() {
try {
const p = await getProduct(1);
console.log(p.name);
} catch (err) {
console.log('Error:', err.message);
}
try {
await getProduct(99999);
} catch (err) {
console.log('Error:', err.message); // HTTP 404
}
}
main();
Обработать 401 Unauthorized
#Попробуйте запросить `/api/users/profile/` без токена. Проверьте `response.status` — если это 401, выведите конкретное сообщение. Иначе выведите данные.
const BASE_URL = 'https://apilearn.tukas.dev';
async function main() {
// запрос профиля без авторизации, отдельная обработка 401
}
main();
Решение
const BASE_URL = 'https://apilearn.tukas.dev';
async function main() {
const response = await fetch(`${BASE_URL}/api/users/profile/`);
if (response.status === 401) {
console.log('Unauthorized — provide a valid token');
} else if (!response.ok) {
console.log(`HTTP error: ${response.status}`);
} else {
const data = await response.json();
console.log(data.username);
}
}
main();
Обёртка safeFetch
#Напишите функцию `safeFetch(url)`, которая проверяет `response.ok` и возвращает `null` (выводя ошибку) при сбое, или разобранный JSON при успехе. Проверьте с действительным URL и URL, возвращающим 404.
const BASE_URL = 'https://apilearn.tukas.dev';
async function safeFetch(url) {
// fetch, проверить ok, вернуть json или null
}
async function main() {
const good = await safeFetch(`${BASE_URL}/api/products/1/`);
console.log(good ? good.name : 'null');
const bad = await safeFetch(`${BASE_URL}/api/products/99999/`);
console.log(bad);
}
main();
Решение
const BASE_URL = 'https://apilearn.tukas.dev';
async function safeFetch(url) {
try {
const response = await fetch(url);
if (!response.ok) {
console.error(`HTTP ${response.status} for ${url}`);
return null;
}
return response.json();
} catch (err) {
console.error('Network error:', err.message);
return null;
}
}
async function main() {
const good = await safeFetch(`${BASE_URL}/api/products/1/`);
console.log(good ? good.name : 'null');
const bad = await safeFetch(`${BASE_URL}/api/products/99999/`);
console.log(bad);
}
main();
Таймаут через AbortController
#Напишите функцию `fetchWithTimeout(url, ms)`, которая прерывает запрос, если он выполняется дольше `ms` миллисекунд. Проверьте на обычном запросе и выведите результат или `"timed out"`.
const BASE_URL = 'https://apilearn.tukas.dev';
async function fetchWithTimeout(url, ms = 3000) {
// использовать AbortController для прерывания через ms миллисекунд
}
async function main() {
const data = await fetchWithTimeout(`${BASE_URL}/api/products/`, 5000);
console.log(data ? data.count : 'timed out');
}
main();
Решение
const BASE_URL = 'https://apilearn.tukas.dev';
async function fetchWithTimeout(url, ms = 3000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), ms);
try {
const response = await fetch(url, { signal: controller.signal });
clearTimeout(timer);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return response.json();
} catch (err) {
if (err.name === 'AbortError') {
console.log('Request timed out');
return null;
}
throw err;
}
}
async function main() {
const data = await fetchWithTimeout(`${BASE_URL}/api/products/`, 5000);
console.log(data ? data.count : 'timed out');
}
main();