until для промисов

Haunted — это современная библиотека для создания веб-компонентов с использованием реактивного подхода на базе хуков, похожих на React. Одной из ключевых задач при работе с компонентами является корректная обработка асинхронных данных, например промисов. Хук until решает эту задачу, позволяя отложенно рендерить результат промиса, одновременно предоставляя возможность отображения промежуточного состояния.


Основная идея until

Хук until используется для рендеринга содержимого на основе промиса. Он принимает два аргумента:

  1. Promise или значение — асинхронная операция, результат которой должен быть отображён.
  2. Фоллбэк — содержимое, которое рендерится до завершения промиса.

Синтаксис:

import { html, until } from 'haunted';

const Component = () => {
  const dataPromise = fetch('/api/data').then(res => res.json());

  return html`
    <div>
      ${until(dataPromise.then(data => html`<p>${data.message}</p>`), html`<p>Загрузка...</p>`)}
    </div>
  `;
};

В этом примере до получения ответа от API отображается текст Загрузка.... Как только промис завершится, содержимое заменяется на результат запроса.


Поддержка нескольких состояний

until особенно полезен при работе с компонентами, где асинхронные данные могут находиться в нескольких состояниях: загрузка, успех, ошибка. Для обработки ошибок промиса можно использовать метод catch:

const dataPromise = fetch('/api/data')
  .then(res => res.json())
  .catch(err => html`<p>Ошибка загрузки данных: ${err.message}</p>`);

return html`
  ${until(dataPromise, html`<p>Загрузка данных...</p>`)}
`;

В этом подходе фоллбэк показывается до получения результата, а в случае ошибки рендерится альтернативное сообщение.


Использование с реактивными хуками

Haunted позволяет создавать реактивные состояния с помощью useState и других хуков. until можно комбинировать с useState для динамического обновления данных:

import { useState } from 'haunted';

const AsyncComponent = () => {
  const [url, setUrl] = useState('/api/data');

  const dataPromise = fetch(url).then(res => res.json());

  return html`
    <div>
      ${until(
        dataPromise.then(data => html`<p>${data.message}</p>`),
        html`<p>Загрузка...</p>`
      )}
      <button @click=${() => setUrl('/api/other')}>Загрузить другие данные</button>
    </div>
  `;
};

При смене URL автоматически перерендеривается содержимое компонента после завершения нового промиса.


Комбинирование нескольких промисов

until может обрабатывать один промис, но для работы с несколькими асинхронными источниками часто используют Promise.all:

const data1 = fetch('/api/data1').then(res => res.json());
const data2 = fetch('/api/data2').then(res => res.json());

return html`
  ${until(
    Promise.all([data1, data2]).then(([d1, d2]) => html`
      <p>Первый источник: ${d1.message}</p>
      <p>Второй источник: ${d2.message}</p>
    `),
    html`<p>Загрузка нескольких источников...</p>`
  )}
`;

Такой подход позволяет рендерить только тогда, когда все промисы завершены, сохраняя чистоту кода и предотвращая множественные промежуточные состояния.


Ленивая загрузка компонентов

until подходит не только для данных, но и для динамического импорта модулей:

const LazyComponent = () => html`
  ${until(
    import('./heavy-component.js').then(module => module.default()),
    html`<p>Компонент загружается...</p>`
  )}
`;

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


Ключевые моменты использования

  • until не блокирует рендеринг: компонент отображает фоллбэк сразу, промис завершится асинхронно.
  • Фоллбэк может быть любым HTML-шаблоном, включая сложные элементы или другие хуки.
  • Рекомендуется обрабатывать ошибки промисов внутри then/catch для предотвращения сбоев рендеринга.
  • Можно использовать с динамическими данными и реактивными хуками, что делает его гибким инструментом для асинхронного рендеринга.

until превращает работу с промисами в Haunted в предсказуемый и удобный процесс, обеспечивая плавный переход между состояниями загрузки, успешного получения данных и ошибок без громоздких конструкций.