Современные подходы к асинхронной загрузке данных на JavaScript

Раздел: Веб-разработка -> создание веб-сайтов

Основные подходы к асинхронным запросам

Как эффективно загружать данные с сервера без перезагрузки страницы?

Современный стандарт предлагает использовать Fetch API в сочетании с синтаксисом async/await. Это читаемый и гибкий способ обмена данными.

async function loadUsers() {\n  try {\n    const response = await fetch('https://jsonplaceholder.typicode.com/users');\n    if (!response.ok) throw new Error('Ошибка HTTP: ' + response.status);\n    const users = await response.json();\n    users.forEach(user => {\n      const li = document.createElement('li');\n      li.textContent = user.name;\n      document.getElementById('userList').appendChild(li);\n    });\n  } catch (error) {\n    console.error('Не удалось загрузить пользователей:', error);\n  }\n}\nloadUsers();

Javascript веб (веб-разработка на javascript)

Типичная ошибка: забыть проверить response.ok. В результате при ошибке сервера (404, 500) код продолжает работать с некорректными данными. Также важно обрабатывать исключения с try...catch.

Цель использования: получение JSON, текста, Blob или другого ресурса с последующим обновлением DOM. Подходит для большинства AJAX-запросов.

Как выполнить асинхронный запрос без современных API?

Классический XMLHttpRequest всё ещё работает во всех браузерах. Он использует события и callback-функции.

const xhr = new XMLHttpRequest();\nxhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);\nxhr.onload = function() {\n  if (xhr.status >= 200 && xhr.status < 300) {\n    const post = JSON.parse(xhr.responseText);\n    document.getElementById('postTitle').textContent = post.title;\n  } else {\n    console.error('Ошибка загрузки:', xhr.status);\n  }\n};\nxhr.onerror = function() {\n  console.error('Сетевая ошибка');\n};\nxhr.send();

шаблон html css js (создание сайта на html/css/js)

Проблемы: более громоздкий код, необходимость вручную парсить JSON, обработка разных состояний (onload, onerror, ontimeout). Возможна утечка памяти при отмене запроса.

Цель использования: поддержка старых браузеров или проекты, где Fetch недоступен.

Как обработать асинхронный запрос с помощью цепочки промисов?

Fetch возвращает промис, поэтому можно не использовать async/await, а соединять .then() и .catch().

fetch('https://jsonplaceholder.typicode.com/comments?postId=1')\n  .then(response => {\n    if (!response.ok) throw new Error('HTTP ' + response.status);\n    return response.json();\n  })\n  .then(comments => {\n    comments.slice(0, 3).forEach(comment => {\n      const p = document.createElement('p');\n      p.textContent = comment.body;\n      document.getElementById('comments').appendChild(p);\n    });\n  })\n  .catch(error => console.error(error));

Ошибка: забыть вернуть промис внутри .then() – следующий .then() получит undefined.

Цель: когда не хочется использовать async/await, например в простых скриптах.

Как упростить HTTP-запросы с помощью библиотеки?

Библиотека Axios предоставляет лаконичный интерфейс и автоматическую обработку JSON.

axios.get('https://jsonplaceholder.typicode.com/todos/1')\n  .then(response => {\n    document.getElementById('todo').textContent = response.data.title;\n  })\n  .catch(error => {\n    console.error('Ошибка Axios:', error.response?.status);\n  });

Недостаток: нужно подключать внешнюю зависимость. Возможны конфликты версий.

Цель: проекты с интенсивными запросами, где нужны интерцепторы, отмена запросов, автоматическая трансформация данных.

Как организовать двустороннюю связь в реальном времени?

WebSocket устанавливает постоянное соединение с сервером, позволяя обмениваться данными мгновенно.

const socket = new WebSocket('ws://example.com/chat');\nsocket.onopen = () => {\n  socket.send(JSON.stringify({type: 'join', room: 'general'}));\n};\nsocket.onmessage = (event) => {\n  const msg = JSON.parse(event.data);\n  const div = document.createElement('div');\n  div.textContent = msg.user + ': ' + msg.text;\n  document.getElementById('chat').appendChild(div);\n};\nsocket.onerror = (err) => console.error('WebSocket error', err);

Проблемы: WebSocket не работает через обычные прокси, требует поддержки сервера. При обрыве соединения нужно реализовать реконнект.

Цель: чаты, игры, котировки акций, совместное редактирование.

Расширенные примеры кода

Ниже приведены более сложные сценарии использования асинхронных запросов.

1. Отправка формы с файлом через FormData и Fetch

Пример
const form = document.getElementById('uploadForm');\nform.addEventListener('submit', async (e) => {\n  e.preventDefault();\n  const formData = new FormData(form);\n  try {\n    const response = await fetch('/upload', {\n      method: 'POST',\n      body: formData\n    });\n    if (response.ok) {\n      const result = await response.json();\n      console.log('Файл загружен:', result.url);\n    } else {\n      console.error('Ошибка загрузки', response.status);\n    }\n  } catch (err) {\n    console.error('Сетевая ошибка', err);\n  }\n});
(В консоль выводится URL загруженного файла)

Пояснение: FormData автоматически кодирует поля и файлы в multipart/form-data. Не нужно устанавливать заголовок Content-Type, браузер сделает это сам.

2. Запрос с таймаутом и отменой через AbortController

Пример
function fetchWithTimeout(url, timeout = 5000) {\n  const controller = new AbortController();\n  const timer = setTimeout(() => controller.abort(), timeout);\n  return fetch(url, { signal: controller.signal })\n    .finally(() => clearTimeout(timer));\n}\n\nfetchWithTimeout('https://jsonplaceholder.typicode.com/users', 3000)\n  .then(response => response.json())\n  .then(data => console.log(data))\n  .catch(err => {\n    if (err.name === 'AbortError') {\n      console.error('Запрос отменён по таймауту');\n    } else {\n      console.error('Ошибка:', err);\n    }\n  });
(При превышении таймаута в консоли 'Запрос отменён по таймауту')

Пояснение: AbortController позволяет отменить один или несколько fetch-запросов. Полезно при уходе пользователя со страницы.

3. Прогресс загрузки файла через XMLHttpRequest

Пример
const xhr = new XMLHttpRequest();\nxhr.open('POST', '/upload', true);\nconst formData = new FormData();\nformData.append('file', document.getElementById('fileInput').files[0]);\n\nxhr.upload.onprogress = (event) => {\n  if (event.lengthComputable) {\n    const percent = Math.round((event.loaded / event.total) * 100);\n    document.getElementById('progressBar').value = percent;\n    document.getElementById('progressText').textContent = percent + '%';\n  }\n};\nxhr.onload = () => {\n  if (xhr.status === 200) {\n    console.log('Загрузка завершена');\n  }\n};\nxhr.send(formData);
(Анимируется прогресс бар и отображается процент)

Пояснение: XMLHttpRequest предоставляет событие upload.onprogress для отслеживания отправки. Fetch не поддерживает отслеживание прогресса отправки (только получения через ReadableStream).

4. Повторные попытки при ошибках (retry)

Пример
async function fetchWithRetry(url, options = {}, retries = 3, delay = 1000) {\n  for (let i = 0; i < retries; i++) {\n    try {\n      const response = await fetch(url, options);\n      if (!response.ok) throw new Error('HTTP ' + response.status);\n      return await response.json();\n    } catch (err) {\n      if (i === retries - 1) throw err;\n      console.warn('Попытка ' + (i+1) + ' не удалась, повтор через ' + delay + 'ms');\n      await new Promise(resolve => setTimeout(resolve, delay));\n      delay *= 2; // экспоненциальная задержка\n    }\n  }\n}\n\nfetchWithRetry('https://jsonplaceholder.typicode.com/posts/1', {}, 3, 500)\n  .then(data => console.log('Успех:', data))\n  .catch(err => console.error('Ошибка после всех попыток:', err));
(В случае неудач выводятся сообщения о попытках; при успехе - данные)

Пояснение: Полезно при нестабильном соединении. Важно не перегружать сервер, поэтому используется экспоненциальная задержка.

5. Параллельная загрузка нескольких ресурсов с Promise.all

Пример
async function loadMultiple() {\n  const urls = [\n    'https://jsonplaceholder.typicode.com/posts',\n    'https://jsonplaceholder.typicode.com/users',\n    'https://jsonplaceholder.typicode.com/comments'\n  ];\n  try {\n    const responses = await Promise.all(urls.map(url => fetch(url)));\n    const data = await Promise.all(responses.map(res => res.json()));\n    console.log('Посты:', data[0].length);\n    console.log('Пользователи:', data[1].length);\n    console.log('Комментарии:', data[2].length);\n  } catch (error) {\n    console.error('Один из запросов упал:', error);\n  }\n}\nloadMultiple();
(В консоли отображаются длины массивов)

Пояснение: Promise.all ждёт все промисы. Если хотя бы один отклоняется, весь Promise.all отклоняется. Для частичной устойчивости используйте Promise.allSettled.

веб-разработка на JavaScript - comments

En
Javascript веб (javascript)