Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти Інструменти Події
Інше
Про нас Реклама

Що таке асинхронні дії (#[Async]) у Livewire 4 і чому вони небезпечні для стану?

За замовчуванням дії одного компонента виконуються по черзі: поки йде запит, наступні чекають. Асинхронна дія виконується одразу, паралельно з іншими, - не чекає черги й нікого не блокує.

use Livewire\Attributes\Async;

new class extends Component {
    #[Async]
    public function trackClick(string $url): void
    {
        Analytics::track('external-link', ['url' => $url, 'user' => auth()->id()]);
    }
};
<a href="{{ $url }}" target="_blank" wire:click.async="trackClick('{{ $url }}')">Перейти</a>

Або точково в шаблоні: wire:click.async, wire:intersect.async.

Чому стан і асинхронність несумісні. Кожен запит стартує зі знімка стану, який браузер мав на момент відправки. Паралельні запити не бачать результатів одне одного:

#[Async]   // так не можна
public function increment(): void
{
    $this->count++;
}

П'ять швидких кліків - п'ять запитів, кожен починає з count = 0, кожен повертає 1. Лічильник показує 1 замість 5. Зміни губляться, а остаточний стан визначає відповідь, що прийшла останньою, - не обов'язково остання за часом дія.

Безпечні сценарії:

  • чисті побічні ефекти: аналітика, журнали, постановка джоби в чергу, сповіщення - дія не змінює властивостей, що відображаються;
  • дані для JavaScript: результат повертається в Alpine і зберігається там, а не в стані компонента:
<div x-data="{ suggestions: [] }">
    <input x-on:input.debounce="suggestions = await $wire.fetchSuggestions($event.target.value)">
</div>

Правило: якщо асинхронна дія присвоює значення публічній властивості, що впливає на шаблон, - це помилка дизайну.

Захист від гонитви на сервері асинхронність не скасовує: дві паралельні дії, що змінюють один запис у базі, потребують атомарних операцій (increment()), транзакцій чи блокувань, як і будь-які паралельні HTTP-запити.

Зв'язок з іншими механізмами:

  • з #[Renderless] - класичне поєднання для «запустив і забув»;
  • #[Isolate] - про групування між компонентами, а #[Async] - про чергу всередині компонента;
  • острівці теж ходять паралельно, і застереження про спільний стан для них те саме.

Докладніше в документації: Атрибут Async

Схожі питання