You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: llms-full.txt
+201-2Lines changed: 201 additions & 2 deletions
Original file line number
Diff line number
Diff line change
@@ -624,7 +624,138 @@ await main(function*() {
624
624
625
625
---
626
626
627
-
## 10. Critical Gotchas (Quick Reference)
627
+
## 10. Events: on() and once()
628
+
629
+
### Single Events with once()
630
+
631
+
Wait for a single event from any [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) (DOM elements, WebSockets, etc.):
632
+
633
+
```js
634
+
import { main, once } from 'effection';
635
+
636
+
await main(function*() {
637
+
let socket = new WebSocket('ws://localhost:1234');
638
+
639
+
yield* once(socket, 'open');
640
+
console.log('socket is open!');
641
+
642
+
let closeEvent = yield* once(socket, 'close');
643
+
console.log('closed with code', closeEvent.code);
644
+
});
645
+
```
646
+
647
+
### Event Streams with on()
648
+
649
+
For recurring events, use `on()` which returns a Stream (avoids missing events between `once()` calls):
650
+
651
+
```js
652
+
import { main, on, each } from 'effection';
653
+
654
+
await main(function*() {
655
+
let socket = new WebSocket('ws://localhost:1234');
656
+
657
+
for (let message of yield* each(on(socket, 'message'))) {
658
+
console.log('message:', message.data);
659
+
yield* each.next();
660
+
}
661
+
});
662
+
```
663
+
664
+
**Note:** `on()` and `once()` work with the DOM `EventTarget` API only. For Node.js EventEmitters, you'll need to wrap them or use a different approach.
665
+
666
+
**Type inference:** Both functions infer the correct event type from the target (e.g., `CloseEvent` for `'close'` on WebSocket).
667
+
668
+
---
669
+
670
+
## 11. Utility Operations
671
+
672
+
### suspend() - Pause Indefinitely
673
+
674
+
Pause execution until the scope is destroyed. Useful for "wait forever" patterns:
675
+
676
+
```js
677
+
import { main, suspend } from "effection";
678
+
679
+
await main(function*() {
680
+
try {
681
+
console.log('suspending');
682
+
yield* suspend(); // Waits here until scope ends
683
+
} finally {
684
+
console.log('done!'); // Runs when scope is destroyed
685
+
}
686
+
});
687
+
```
688
+
689
+
Common use: Keep a resource alive until parent scope ends.
690
+
691
+
### ensure() - Guaranteed Cleanup
692
+
693
+
Cleaner alternative to `try/finally` for cleanup. Avoids rightward drift:
| `yield* call(fn)` for boundaries | `yield* scoped(fn)` |
779
+
780
+
**Important:** `call()` no longer establishes concurrency/error boundaries. Spawned tasks inside `call()` are NOT terminated when it returns. Use `scoped()` if you need boundary behavior.
781
+
782
+
### `action()` Simplified
783
+
784
+
v4 `action()` now mirrors `new Promise()` more closely:
785
+
786
+
```js
787
+
// v3 - generator function with try/finally
788
+
function sleep(ms) {
789
+
return action(function*(resolve) {
790
+
let timeout = setTimeout(resolve, ms);
791
+
try {
792
+
yield* suspend();
793
+
} finally {
794
+
clearTimeout(timeout);
795
+
}
796
+
});
797
+
}
798
+
799
+
// v4 - synchronous function, return cleanup
800
+
function sleep(ms) {
801
+
return action((resolve, reject) => {
802
+
let timeout = setTimeout(resolve, ms);
803
+
return () => clearTimeout(timeout); // cleanup function
804
+
});
805
+
}
806
+
```
807
+
808
+
Like `call()`, `action()` no longer establishes boundaries. Wrap with `scoped()` if needed.
809
+
810
+
### Task Execution Priority Change
811
+
812
+
**v3:** Child tasks started immediately when spawned
813
+
**v4:** Parent tasks always have priority; children wait until parent yields to async operation
814
+
815
+
```js
816
+
yield* spawn(childTask);
817
+
console.log("In v4, child hasn't started yet");
818
+
yield* sleep(0); // NOW child gets to run
819
+
```
820
+
821
+
This makes execution more predictable but may affect timing-sensitive code. Add `yield* sleep(0)` to explicitly yield control if children need to start immediately.
822
+
823
+
---
824
+
637
825
## Quick Reference Summary
638
826
639
827
**Entry points:**
@@ -643,6 +831,8 @@ await main(function*() {
643
831
**Core operations:**
644
832
- `sleep(ms)` - Pause for duration
645
833
- `spawn(fn)` - Concurrent child operation
834
+
- `suspend()` - Pause indefinitely until scope ends
835
+
- `ensure(fn)` - Guaranteed cleanup (cleaner than try/finally)
646
836
647
837
**Callback integration:**
648
838
- `withResolvers()` - Create operation from callback (like `Promise.withResolvers()`)
@@ -660,10 +850,19 @@ await main(function*() {
660
850
- `each(stream)` - Loop over stream values (requires `yield* each.next()`)
661
851
- `createSignal()` - Stream from external events
662
852
853
+
**Events (EventTarget only):**
854
+
- `once(target, name)` - Wait for single event
855
+
- `on(target, name)` - Stream of events
856
+
857
+
**Types:**
858
+
- `Result<T>` - `{ ok: true, value }` or `{ ok: false, error }`
859
+
- `Maybe<T>` - `{ exists: true, value }` or `{ exists: false }`
0 commit comments