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());
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");
}
}
Особенности:
loading позволяет визуально
показывать процесс запроса.finally гарантирует снятие индикатора даже при
ошибках.Stimulus позволяет отправлять собственные события для уведомления других контроллеров о завершении асинхронной операции:
this.dispatch("dataLoaded", { detail: { data } });
dataLoaded можно слушать в другом контроллере
через
data-action="example:dataLoaded->other#handleData".Асинхронные действия часто сопровождаются сетевыми и логическими ошибками. Для надежной работы важно использовать:
try/catch внутри асинхронных методов.response.ok.Stimulus обеспечивает легкую интеграцию асинхронных действий с
контроллерами и DOM. Использование Promise,
async/await, а также событийной модели позволяет строить
динамичные, отзывчивые интерфейсы без громоздких структур, сохраняя
простоту и читаемость кода.