Перейти к содержимому

JavaScript Fetch: прогресс загрузки

В современной веб-разработке эффективная обработка HTTP-запросов имеет первостепенное значение. Fetch API стал основным решением для выполнения сетевых запросов в JavaScript. В этой статье мы подробно разберем работу с Fetch API, уделив особое внимание отслеживанию прогресса загрузки. Мы рассмотрим различные методы, приведем примеры из реальной практики и поделимся профессиональными советами, которые помогут вам лучше понять и внедрить Fetch API в свои проекты.

Отслеживание прогресса загрузки

Одной из расширенных возможностей Fetch API является отслеживание прогресса загрузки. Это особенно полезно при работе с большими файлами или при необходимости предоставления обратной связи пользователю во время длительных операций.

Использование ReadableStream

Для отслеживания прогресса загрузки мы можем использовать интерфейс ReadableStream. Это позволяет читать тело ответа порциями и контролировать процесс загрузки.

Примечание: ReadableStream широко поддерживается в современных браузерах. Для поддержки устаревших браузеров рассмотрите использование API XMLHttpRequest с его событием onprogress.


Output appears here after Run.

Функция fetchWithProgress загружает данные по указанному URL и отслеживает прогресс загрузки. Она читает тело ответа порциями с помощью интерфейса ReadableStream и вызывает колбэк onProgress, передавая ему полученную длину и общую длину контента. Затем загруженные данные собираются обратно из порций и возвращаются в виде декодированной строки.

note

Функция пытается автоматически прочитать заголовок Content-Length. Если заголовок отсутствует (что часто бывает при чанковой передаче или сжатии), используется параметр fallbackSize. В продакшене разработчик или сервер должен предоставлять это значение по умолчанию. Для больших файлов избегайте буферизации всех порций в памяти; обрабатывайте их постепенно или используйте response.blob() для нетекстовых ответов.

Отображение прогресса пользователю

Чтобы предоставить обратную связь пользователю, мы можем интегрировать индикатор прогресса, который обновляется по мере загрузки.


html
<body>
  <div id="progress-bar" style="width: 100%; background-color: #e0e0e0;">
    <div id="progress" style="width: 0; height: 20px; background-color: #76c7c0;"></div>
  </div>
  <div id="output"></div>
  <script>
    function updateProgressBar(received, total) {
      const progressElement = document.getElementById('progress');
      const percentage = Math.min(100, (received / total) * 100);
      progressElement.style.width = percentage + '%';
    }
    document.addEventListener('DOMContentLoaded', () => {
      const url = 'https://www.w3docs.com/uploads/media/default/0001/05/dd10c28a7052fb6d2ff13bc403842b797a73ff3b.txt';
      const size = 3_900_000; // fallback size
      // fetchWithProgress is defined in the previous code block
      fetchWithProgress(url, updateProgressBar, size)
      .then(data => {
        document.getElementById('output').textContent = 'File content: ' + data.slice(0, 1000) + '...';
      })
      .catch(err => console.error("Download failed:", err));
    });
  </script>
</body>

WARNING

При загрузке очень больших файлов избегайте слишком частого обновления интерфейса. Например, вместо обновления индикатора прогресса для каждой отдельной порции, можно обновлять его реже (например, каждые несколько порций или по временному интервалу). Это поможет сохранить интерфейс легковесным.

HTML-элементы в приведенном выше примере создают индикатор прогресса для визуализации процесса загрузки и область для отображения загруженного содержимого. div с классом progress-bar служит контейнером, а div с классом progress отображает фактический прогресс загрузки.

Код JavaScript обновляет индикатор прогресса на основе полученной длины данных и общей длины контента. Функция updateProgressBar вычисляет процент загруженных данных и соответствующим образом изменяет ширину индикатора. Обработчик событий запускает функцию fetchWithProgress при загрузке страницы, обновляя индикатор прогресса и отображая загруженное содержимое в элементе output.

Примечание: fetchWithProgress определена в предыдущем фрагменте кода. В реальном проекте убедитесь, что она доступна в текущей области видимости (например, через импорт модулей, сборку или глобальный тег <script>).

Профессиональные советы

  • Работа с бинарными данными: TextDecoder работает только с текстом. Для бинарных файлов используйте response.blob() или response.arrayBuffer().
  • Ограничение частоты обновлений: Частое чтение порций может заблокировать поток UI. Используйте throttle или debounce для колбэка прогресса.
  • Управление памятью: Избегайте хранения всех порций в массиве для больших файлов. Обрабатывайте их постепенно.
  • Резервный Content-Length: Заголовок часто отсутствует. Всегда предоставляйте надежное значение fallbackSize.
  • Оптимизация обратной связи: Предоставление обратной связи о прогрессе загрузки в реальном времени повышает удовлетворенность пользователей и делает приложение более отзывчивым.
  • Использование возможностей браузеров: Разные браузеры могут по-разному поддерживать расширенные функции. Тестируйте вашу реализацию в нескольких браузерах для обеспечения совместимости.
  • Упрощайте, когда возможно: Для загрузок, где потоковое отслеживание прогресса не является строго обязательным, response.arrayBuffer() предлагает более простой способ восстановления данных без ручного объединения порций.

Имея эти знания и примеры, вы готовы внедрить Fetch API с отслеживанием прогресса загрузки в свои проекты, предлагая пользователям бесшовный и информативный опыт.

Заключение

Освоение Fetch API подразумевает понимание не только того, как выполнять базовые запросы, но и как обрабатывать более сложные сценарии, такие как отслеживание прогресса загрузки. Используя ReadableStream, мы можем контролировать загрузку и предоставлять обратную связь, что значительно улучшает пользовательский опыт. Внедрение этих техник обеспечит надежность, удобство использования и эффективную обработку больших объемов данных в ваших приложениях.

Практика

Каковы ключевые шаги для отслеживания прогресса загрузки с помощью Fetch API?

Считаете ли это полезным?

Предпросмотр dual-run — сравните с маршрутами Symfony на продакшене.