Skip to content

Commit bdf355e

Browse files
committed
Added suspend, on/once, and Result to llms-full.txt
1 parent 028e735 commit bdf355e

File tree

1 file changed

+201
-2
lines changed

1 file changed

+201
-2
lines changed

llms-full.txt

Lines changed: 201 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -624,7 +624,138 @@ await main(function*() {
624624

625625
---
626626

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:
694+
695+
```js
696+
import { main, ensure } from 'effection';
697+
import { createServer } from 'http';
698+
699+
await main(function*() {
700+
let server = createServer(...);
701+
yield* ensure(() => { server.close() }); // Runs when scope ends
702+
703+
// ... rest of your code
704+
});
705+
```
706+
707+
For async cleanup, return an operation:
708+
709+
```js
710+
yield* ensure(function*() {
711+
server.close();
712+
yield* once(server, 'close'); // Wait for close to complete
713+
});
714+
```
715+
716+
---
717+
718+
## 12. Result and Maybe Types
719+
720+
### Result<T> - Success or Error
721+
722+
Used for operations that can succeed or fail:
723+
724+
```js
725+
type Result<T> =
726+
| { ok: true, value: T } // Success
727+
| { ok: false, error: Error } // Failure
728+
729+
// Create results:
730+
import { Ok, Err } from 'effection';
731+
732+
Ok(42) // { ok: true, value: 42 }
733+
Ok() // { ok: true } (for void)
734+
Err(new Error()) // { ok: false, error: Error }
735+
```
736+
737+
### Maybe<T> - Value or Nothing
738+
739+
Used by `scoped()` when an operation is halted before returning:
740+
741+
```js
742+
type Maybe<T> =
743+
| { exists: true, value: T } // Has value
744+
| { exists: false } // No value (operation was halted)
745+
746+
// Example: scoped() returns Maybe when halted
747+
let result = yield* scoped(function*() {
748+
yield* suspend(); // Never completes
749+
return "value";
750+
});
751+
// result = { exists: false } because operation was halted
752+
```
753+
754+
This explains the `{ exists: false }` you may see when a scoped operation is cancelled before it can return.
755+
756+
---
757+
758+
## 13. Critical Gotchas (Quick Reference)
628759

629760
1. **Operations are lazy** - Must use `yield*`, `run()`, or `spawn()` to execute (see Section 3)
630761
2. **`each.next()` required** - Always call `yield* each.next()` at end of `for...of` loop (see Section 9)
@@ -634,6 +765,63 @@ await main(function*() {
634765

635766
---
636767

768+
## 14. Upgrading from v3 to v4
769+
770+
### `call()` Simplified
771+
772+
v4 `call()` only invokes functions—nothing else. Use new helpers for other cases:
773+
774+
| v3 Usage | v4 Replacement |
775+
|----------|----------------|
776+
| `yield* call(promise)` | `yield* until(promise)` |
777+
| `yield* call(5)` | `yield* constant(5)` |
778+
| `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+
637825
## Quick Reference Summary
638826

639827
**Entry points:**
@@ -643,6 +831,8 @@ await main(function*() {
643831
**Core operations:**
644832
- `sleep(ms)` - Pause for duration
645833
- `spawn(fn)` - Concurrent child operation
834+
- `suspend()` - Pause indefinitely until scope ends
835+
- `ensure(fn)` - Guaranteed cleanup (cleaner than try/finally)
646836

647837
**Callback integration:**
648838
- `withResolvers()` - Create operation from callback (like `Promise.withResolvers()`)
@@ -660,10 +850,19 @@ await main(function*() {
660850
- `each(stream)` - Loop over stream values (requires `yield* each.next()`)
661851
- `createSignal()` - Stream from external events
662852

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 }`
860+
663861
**Conversion:**
664862
- `until(promise)` - Promise → Operation (use inside operations)
665863
- `run(operation)` - Operation → Promise (use in async context)
666-
- `call(asyncFn)` - Call async function from Effection
864+
- `call(fn)` - Invoke function as operation (v4: no longer establishes boundaries)
865+
- `constant(value)` - Wrap constant value as operation
667866

668867
**Remember:**
669868
- Use `yield*` not `await` (inside operations)

0 commit comments

Comments
 (0)