Skip to content

infinite loop and page freeze caused by async Alpine.effect#1

Merged
jissereitsma merged 1 commit intomasterfrom
bug/async-localstorage-effect
Mar 2, 2026
Merged

infinite loop and page freeze caused by async Alpine.effect#1
jissereitsma merged 1 commit intomasterfrom
bug/async-localstorage-effect

Conversation

@allrude
Copy link
Contributor

@allrude allrude commented Mar 2, 2026

WHY:
Since version 1.1.19 (Feb 16, 2026), Alpine.store('LocalStorage').get() became asynchronous, but the Message store was using it incorrectly inside Alpine.effect(), causing:

  1. Infinite reactivity loops - Alpine.effect() expects synchronous functions for proper dependency tracking. Using async causes the effect to return a Promise immediately, preventing Alpine from tracking reactive dependencies.

  2. The effect modifies this.messages inside itself, triggering re-runs infinitely as Alpine tries to track the dependency on this.messages.

  3. Page freezes and high CPU usage as the browser gets stuck in the loop.

  4. saveMessage() was calling getMessagesFromStore() without await, causing it to try pushing to a Promise instead of an array.

HOW:

  1. Replaced Alpine.effect(async () => {...}) with a single-run event listener:

    • Listen for 'loki:init:localstorage-store' event (dispatched by LocalStorage store when initialized)
    • Use { once: true } to ensure it only runs once
    • Load messages asynchronously without reactive tracking issues
    • No infinite loops since we're not inside Alpine's reactivity system
  2. Made saveMessage() async and added await before getMessagesFromStore() to properly handle the Promise returned by the async method.

RESULT:

  • Messages load correctly from LocalStorage once on initialization
  • No infinite loops or page freezes
  • Proper async/await pattern throughout
  • Compatible with async LocalStorage.get() from base 1.1.19+

WHY:
Since version 1.1.19 (Feb 16, 2026), Alpine.store('LocalStorage').get()
became asynchronous, but the Message store was using it incorrectly inside
Alpine.effect(), causing:

1. Infinite reactivity loops - Alpine.effect() expects synchronous functions
   for proper dependency tracking. Using async causes the effect to return
   a Promise immediately, preventing Alpine from tracking reactive dependencies.

2. The effect modifies this.messages inside itself, triggering re-runs
   infinitely as Alpine tries to track the dependency on this.messages.

3. Page freezes and high CPU usage as the browser gets stuck in the loop.

4. saveMessage() was calling getMessagesFromStore() without await, causing
   it to try pushing to a Promise instead of an array.

HOW:
1. Replaced Alpine.effect(async () => {...}) with a single-run event listener:
   - Listen for 'loki:init:localstorage-store' event (dispatched by
     LocalStorage store when initialized)
   - Use { once: true } to ensure it only runs once
   - Load messages asynchronously without reactive tracking issues
   - No infinite loops since we're not inside Alpine's reactivity system

2. Made saveMessage() async and added await before getMessagesFromStore()
   to properly handle the Promise returned by the async method.

RESULT:
- Messages load correctly from LocalStorage once on initialization
- No infinite loops or page freezes
- Proper async/await pattern throughout
- Compatible with async LocalStorage.get() from base 1.1.19+
@allrude allrude added the bug Something isn't working label Mar 2, 2026
@jissereitsma jissereitsma merged commit 9b1cabd into master Mar 2, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants