Опанувати асинхронність у JavaScript. Навчитися працювати з таймерами (setTimeout, setInterval) та об’єктом Promise для симуляції завантаження даних (товарів) “із сервера”. Створити візуальний індикатор завантаження (Spinner/Loader).
delay(ms) на базі Promise.products в асинхронну функцію fetchProducts().try...catch.Увага: Продовжуємо вдосконалювати “TechShop”. У реальному житті товари не лежать у змінній main.js, вони приходять з бази даних через інтернет, що займає час (від 100 мс до кількох секунд). Користувач не повинен бачити порожній екран.
index.html, всередині контейнера .products-grid (або перед ним), додайте HTML-код для спінера:
<div class="loader-container" id="loader">
<div class="spinner"></div>
<p>Завантаження товарів...</p>
</div>
style.css додайте стилі та CSS-анімацію обертання:
.loader-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 3rem;
}
.spinner {
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid var(--primary-color);
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
/* Клас для приховування */
.hidden {
display: none !important;
}
main.js ви маєте масив const products = [...] та одразу його рендерите.Створіть функцію fetchProducts, яка повертатиме Promise і штучно затримуватиме віддачу масиву на 1.5 секунди (використайте setTimeout):
function fetchProducts() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Штучно симулюємо успішну відповідь від сервера
resolve(products);
// Можна також симулювати помилку розкоментувавши рядок нижче:
// reject(new Error("Не вдалося підключитися до бази даних"));
}, 1500); // 1.5 секунди затримки
});
}
Створіть асинхронну функцію initShop(), яка викличе fetchProducts:
async function initShop() {
const loader = document.getElementById("loader");
const container = document.querySelector(".products-grid");
// 1. Показуємо спінер (він і так видимий по замовчуванню, але переконаємось)
loader.classList.remove("hidden");
container.innerHTML = ""; // Очищаємо сітку
try {
// 2. ЧЕКАЄМО 1.5 секунди на "відповідь сервера"
const data = await fetchProducts();
// 3. Сервер відповів (успіх)! Ховаємо спінер
loader.classList.add("hidden");
// 4. Малюємо карточки товарів (як у ПР №9)
const htmlString = data
.map(
(product) => `
<article class="product-card">... (ваш старий код) </article>
`,
)
.join("");
container.innerHTML = htmlString;
} catch (error) {
// Якщо сталася помилка сервера (reject)
loader.classList.add("hidden");
container.innerHTML = `<p class="error">Помилка: ${error.message}</p>`;
}
}
// Запускаємо наш "двигун"
initShop();
git add . та git commit -m "Add loading spinner and simulate API delay using Promises".main.Сторінка магазину стала “живою” і реалістичною. Замість миттєвого відображення жорстко зашитих даних, вона правильно обробляє асинхронність та інформує користувача про стан очікування, покращуючи User Experience (UX).
setTimeout зупиняв (блокував) виконання всього іншого коду навколо себе?Promise об’єкта. Що означають слова resolve та reject?await і чому його можна використовувати лише всередині async функцій? Чому не можна просто написати const data = setTimeout() // => Promise?try...catch під час роботи з мережею чи Promise’ами?