Асинхронные действия и Promise

Stimulus — это JavaScript-фреймворк, ориентированный на минимализм и работу с HTML через контроллеры. Одной из ключевых задач современных веб-приложений является обработка асинхронных действий: запросов к серверу, задержек и операций с внешними API. Для этого Stimulus интегрируется с механизмами JavaScript, такими как Promise, async/await и fetch.

Контроллеры и асинхронные методы

Контроллер в Stimulus создается через класс, наследуемый от Controller:

import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  static targets = ["output"];

  async loadData() {
    try {
      const response = await fetch("/api/data");
      if (!response.ok) throw new Error("Ошибка загрузки данных");
      const data = await response.json();
      this.outputTarget.textContent = JSON.stringify(data, null, 2);
    } catch (error) {
      console.error(error);
      this.outputTarget.textContent = "Произошла ошибка при загрузке данных";
    }
  }
}

Ключевые моменты:

  • Метод loadData помечен как async, что позволяет использовать await внутри метода.
  • Любой асинхронный запрос возвращает Promise, что позволяет удобно обрабатывать результат и ошибки через try/catch.
  • Использование this.outputTarget обеспечивает прямую работу с DOM-элементами, определенными в data-target.

Связывание асинхронного метода с событиями

Stimulus позволяет связывать методы контроллера с событиями DOM через data-action:

<div data-controller="example">
  <button data-action="click->example#loadData">Загрузить данные</button>
  <pre data-example-target="output"></pre>
</div>

При клике на кнопку вызывается асинхронный метод loadData, который выполняет HTTP-запрос и обновляет содержимое <pre>.

Обработка нескольких асинхронных действий

Для сложных сценариев часто требуется запускать несколько асинхронных операций параллельно или последовательно:

async performMultipleTasks() {
  try {
    const [users, posts] = await Promise.all([
      fetch("/api/users").then(res => res.json()),
      fetch("/api/posts").then(res => res.json())
    ]);
    console.log(users, posts);
  } catch (error) {
    console.error("Ошибка при загрузке данных:", error);
  }
}

Пояснения:

  • Promise.all позволяет запускать несколько промисов параллельно, результат приходит массивом.
  • Ошибка в любом из промисов приведет к срабатыванию блока catch.
  • Для последовательного выполнения используют await поочередно:
const users = await fetch("/api/users").then(res => res.json());
const posts = await fetch("/api/posts").then(res => res.json());

Интеграция с Turbo и Stimulus

Stimulus часто используется совместно с Turbo для динамического обновления страниц без полного перезагрузки. Асинхронные действия в контроллерах позволяют загружать данные и обновлять DOM без нарушений поведения Turbo:

async refreshSection() {
  const response = await fetch("/api/section");
  const html = await response.text();
  this.element.innerHTML = html;
}

Здесь this.element — корневой элемент контроллера, который заменяется новым HTML, полученным от сервера.

Управление состоянием загрузки

Для улучшения UX рекомендуется отслеживать состояние загрузки:

async loadData() {
  this.element.classList.add("loading");
  try {
    const response = await fetch("/api/data");
    const data = await response.json();
    this.outputTarget.textContent = JSON.stringify(data, null, 2);
  } catch (error) {
    this.outputTarget.textContent = "Ошибка загрузки";
  } finally {
    this.element.classList.remove("loading");
  }
}

Особенности:

  • Добавление CSS-класса loading позволяет визуально показывать процесс запроса.
  • Блок finally гарантирует снятие индикатора даже при ошибках.

Использование событий для асинхронного взаимодействия

Stimulus позволяет отправлять собственные события для уведомления других контроллеров о завершении асинхронной операции:

this.dispatch("dataLoaded", { detail: { data } });
  • Событие dataLoaded можно слушать в другом контроллере через data-action="example:dataLoaded->other#handleData".
  • Такой подход упрощает декомпозицию логики и управление состоянием приложения.

Ошибки и их обработка

Асинхронные действия часто сопровождаются сетевыми и логическими ошибками. Для надежной работы важно использовать:

  • try/catch внутри асинхронных методов.
  • Валидацию статуса ответа response.ok.
  • Логирование ошибок для отладки и аналитики.

Вывод

Stimulus обеспечивает легкую интеграцию асинхронных действий с контроллерами и DOM. Использование Promise, async/await, а также событийной модели позволяет строить динамичные, отзывчивые интерфейсы без громоздких структур, сохраняя простоту и читаемость кода.