๐Ÿง  The Power of the when() Method

๐Ÿง  Bridging Sync and Async Domains — The Power of when()

In modern JavaScript, we often juggle two different worlds:

  • The synchronous world of values, arrays, and functions
  • The asynchronous world of Promises, events, and distributed systems

Bridging these domains usually means boilerplate: callbacks, event listeners, timeouts, and manual cleanup.

The when() method provides a unifying abstraction. It makes synchronous and asynchronous flows feel the same—expressed as declarative, chainable logic rather than tangled callbacks.

At its core, when() brings two powers:

  1. Start/stop control

    • In iterations: slice infinite/lazy sequences by event-like conditions.
    • In workflows: start or stop tasks dynamically on external triggers.
  2. Domain promotion

    • Iterables of promises → seamlessly promoted into async iterables of resolved values.
    • Synchronous tasks → promoted into asynchronous workflows that respond to events.

๐Ÿ”„ Iterables: start/stop by predicate

On an iterable, calling when(predicate, start) lets you lazily restrict iteration—no need to precompute everything.

const slice = Each.NATURAL
  .when(n => n == 3, true)   // Start at n = 3
  .when(n => n == 5, false); // Stop at n = 5

console.log([...slice]);     // -> [3, 4]

Just like event handlers gate execution, when() gates iteration.


๐Ÿ”„ Iterables of Promises: turning async into sync-like

On an iterable of Promises, when() promotes it to an AsyncEach of resolved values. Now async values behave just like sync ones:

Each.of(Promise.resolve(1), Promise.resolve(2), Promise.resolve(3))
  .when()              // Each<Promise<number>> → AsyncEach<number>
  .sthen(n => n * 2)   // Process numbers as if they were sync

The gap between sync and async vanishes—you write logic once, and it works across both.


⚙️ Functions and Workflows: events as first-class steps

For functions, when(predicate, emitter, event, timeoutMs) promotes a What into an AsyncWhat. Internally, it manages event listeners, timeouts, and cancellation—but you just chain it declaratively.

This lets you treat events the same way you treat function outputs:

a.sthen(b);

However, with when(), b can wait for an event triggered by a.

a.sthen(b.when(predicate, emitter, event));

Here b doesn’t just consume a’s result. It consumes the consequence of the a action.


๐Ÿ“ก Event-Driven Flows: request–response made effortless

The power of when() really shines in a typical request–response pattern.

Instead of manually wiring event listeners, handling timeouts, or managing cleanup, you can express the workflow declaratively and sequentially, as if it were synchronous:

request.sthen(handleResponse.when(isResponse, ws, "message", 5000));

Here, handleResponse will automatically wait for the event matching isResponse, respecting the 5-second timeout—all without additional boilerplate.

This approach lets you focus on the logic, not the plumbing.

Example

const ws = new WebSocket("wss://chat.example.com");
const requestId = "123";

// Send a request
const sendMessage = () =>
  ws.send(JSON.stringify({ user: "Me", requestId, message: "Hello Alice!" }));

// Handle Alice’s reply
const handleResponse = What.as(evt => {
  const msg = JSON.parse(evt.data);
  console.log("๐Ÿ’ฌ Got reply:", msg.message);
});

// Select exactly Alice’s response
const isResponse = evt => {
  const msg = JSON.parse(evt.data);
  return msg.user === "Alice" && msg.requestId === requestId;
};

// Compose the workflow
const waitForAlice = handleResponse
  .when(isResponse, ws, "message", 5000)
  .else(() => console.log("⏰ Timeout"), "TimeoutError")
  .else(() => console.log("⚠️ Connection error"));

await AsyncWhat.as(sendMessage).sthen(waitForAlice)();

Readable. Declarative. Intent-driven.


⏳ Periodic tasks: start/stop on demand

when() also lets you control recurring tasks the same way you slice sequences:

task.self(Infinity, 1000, 1)                        // repeat forever, every 1000ms
  .when(isStart, emitter, event, undefined, true)   // start when predicate matches
  .when(isEnd, emitter, event, undefined, false);   // stop when predicate matches

Just like with iterables, true means begin here, false means end here.


๐ŸŒ Why It Matters

when() is not just sugar. It’s an abstraction over time, events, and distribution:

  • Promises → values: Each<Promise<T>>  AsyncEach<T>
  • Tasks → workflows: What  AsyncWhat
  • Events → chains: external signals seamlessly embedded in declarative pipelines
  • Distributed systems: chains can span machines yet still read like local logic

✅ Conclusion

With when(), asynchronous programming becomes:

  • Fluent — you write in chains, not callbacks
  • Composable — async, sync, and events mix freely
  • Natural — the code reads like thought

๐Ÿ‘‰ Try when() in your next async workflow and experience the difference.

— @fizzwiz ✨

Comments

Popular posts from this blog

✨ Bridging Natural Language and Code

⚡ Early vs. Late Restriction

๐Ÿงฑ v0.0.0-dev.1