Waitfor Expressions
A waitfor expression pauses a query until something happens, then resumes and produces a value. It can wait for one condition or event, or synchronize a group with any { ... } or all { ... }.
Waiting for a condition
In its condition (predicate) mode, waitfor re-checks an ordinary expression until it is satisfied or a timeout is reached. This mode is a pure language construct — it needs no special capability.
There are four forms:
| Form | Waits until | Returns |
|---|---|---|
waitfor <expr> |
the expression is true | true, or false on timeout |
waitfor exists <expr> |
the expression is non-empty according to exists semantics |
true, or false on timeout |
waitfor not exists <expr> |
the expression is empty or absent | true, or false on timeout |
waitfor value <expr> |
the expression yields anything other than none |
that value, or none on timeout |
waitfor value treats empty strings, arrays, and objects as valid values and returns them immediately. When you need the candidate itself but also require it to be non-empty, add a when condition:
The expression is re-evaluated on every polling cycle, so waitfor value loadStatus() may call loadStatus() several times before it returns a value other than none. When the wait runs out, the result reports the timeout rather than raising an error:
Synchronizing condition groups
Add any or all before a block to evaluate several conditions as one polling operation. The mode before any or all applies to every entry:
The grouped forms return:
| Form | Success condition | Result |
|---|---|---|
waitfor any { ... } |
at least one expression is true | true |
waitfor all { ... } |
every expression is true in the same polling cycle | true |
waitfor exists any/all { ... } |
one/all expressions satisfy exists |
true |
waitfor not exists any/all { ... } |
one/all expressions satisfy not exists |
true |
waitfor value any { ... } |
at least one expression is not none |
the first qualifying value in declaration order |
waitfor value all { ... } |
every expression is not none in the same polling cycle |
an array of values in declaration order |
Polling groups synchronize state. all does not remember an entry that passed in an earlier cycle: every entry must pass together during one cycle. any and all evaluate entries in declaration order and stop evaluating the current cycle as soon as the outcome is known.
Each entry may have its own repeated when conditions. Inside them, . is that entry’s candidate:
timeout, every, backoff, jitter, on timeout, and on error belong to the whole group. A group has one timeout and one polling schedule; entries cannot define separate policies.
Tuning the wait
Several clauses control how the wait behaves:
timeout <duration>— the maximum time to wait, provided as a value coercible to Duration.every <interval>— how often to re-check. A second coercible Duration,every <interval>, <cap>, caps how large the interval can grow. Without this clause, polling defaults to100ms.backoff LINEAR | EXPONENTIAL | none— how the interval between checks grows over time.jitter <0..1>— randomizes the interval to avoid synchronized retries.when <condition>— an additional condition that must also hold; the candidate value is available as..
timeout, every, and its cap accept ordinary expressions, so values can be stored or computed:
timeout, every, and its cap use the canonical Duration conversion rules. Numbers are milliseconds, duration strings may be compound, and singleton lists are converted recursively:
All scheduling results must be non-negative. Conversion failures, overflow, and negative values raise runtime errors.
Recovering from a timeout
By default a timed-out wait returns false (or none for the value form). A recovery clause lets you choose a different result.
Use on error to handle a failure raised while evaluating the condition.
Waiting for an event
In event mode, waitfor subscribes to an event source and waits for a matching event, which it returns as a value.
The conceptual form is WAITFOR EVENT <expression> IN <expression>. Both operands are ordinary expressions: the expression after event must produce a String event name, and the expression after in must produce an observable source. A parameter can be used directly as the source:
Operands can also be composed:
When several top-level in operators could be the source delimiter, the parser uses the last viable one. Therefore waitfor event @a in @b in @source is interpreted as waitfor event (@a in @b) in @source. If the intended source expression itself contains a top-level in, group it explicitly: waitfor event @a in (@b in @source). These examples illustrate parsing only; the evaluated source must still be observable.
The event-name and source expressions are each evaluated once while the event wait is being constructed, before the subscription begins. Inspecting several stream messages does not re-evaluate either operand. An explicit recovery retry starts a new operation attempt and evaluates them again. This differs from waitfor value, whose watched expression is evaluated on every polling cycle.
Event timeouts use the same Duration conversion and non-negative scheduling policy as condition waits.
A when filter accepts only events that match a condition. Inside the filter, the incoming event is available as .. Multiple when clauses must all pass.
Synchronizing event occurrences
Event groups use the existing event entry syntax and may subscribe to different sources:
event any returns the first qualifying event that occurs and closes the other subscriptions. Concurrent events have no declaration-order tie-break. If one source ends without a qualifying event, the other arms keep waiting; if every source ends without a match, the result is none, as with a singular event wait.
event all waits until every subscription has produced one qualifying event. Each arm remains satisfied after its event occurs, and the result is an array in declaration order even when the events arrive in another order:
Every event all arm must produce an event that passes its when filters. If an unmatched source ends, the wait fails immediately because the group can no longer be satisfied. A source ending after its arm has matched does not affect the result.
Event groups synchronize occurrences, unlike polling groups, which synchronize state in one cycle. Event filters are per entry:
All subscriptions are established concurrently. A timeout, cancellation, setup failure, trigger failure, stream error, or completed wait closes every remaining subscription. timeout, trigger, on timeout, and on error apply once to the whole group.
As with a singular event wait, every arm’s event-name and source expressions are evaluated once per operation attempt. The resulting subscriptions are then established concurrently.
Triggering the event
A trigger clause runs statements after the subscription is set up but before waiting begins. For an event group, every subscription is established before the one shared trigger runs. This is how you cause the event you are waiting for without risking a race where it fires before you start listening.
The trigger body can dispatch events and run other statements. See Dispatch Expressions.
A host capability
Event mode only works when the source is an observable value — one that produces a stream of events, provided by a module or the host application, such as a browser page. Condition mode has no such requirement. See Value Capabilities and Host Values.