This site describes solve-engine as it is on main: 2.43.0, which npm does not have yet. npm installs 2.40.0, so a page may show an answer that version does not give yet.
AsyncResolutionBatcher
Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:78
Micro-batches async resolution completions into a single DAG walk + re-evaluation pass.
When multiple promises resolve within the same event-loop tick (e.g., 3 currency rates: USD→GBP, USD→EUR, USD→JPY all return within 2ms), this batcher collapses them into ONE DAG walk and ONE re-execution pass instead of 3 separate ones.
Lifecycle:
- Engine calls
add()after eachresolveAsync()completes queueMicrotask()schedulesflush()for the end of the current tickflush()deduplicates queryKeys, walks DAG once for all resolved keys, topologically sorts affected lines, re-executes, and fires listener events
Perf: For N resolutions in a tick, reduces DAG walks from N to 1 and re-executions from N*avgAffected to totalAffected.
Streaming Architecture
Section titled “Streaming Architecture” * Events are published to a native ReadableStream. Stream-based- consumers read from the stream via
getReader(), gaining built-in - cancellation (
reader.cancel()), proper resource cleanup - (
reader.releaseLock()), and the ability to pipeTo()/pipeThrough()/tee()the event flow.
The stream uses a configurable CountQueuingStrategy with a default
highWaterMark of 64 events to limit the internal buffer size.
Constructors
Section titled “Constructors”Constructor
Section titled “Constructor”new AsyncResolutionBatcher( dag, lineCache, vm, highWaterMark?): AsyncResolutionBatcher;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:261
Parameters
Section titled “Parameters”| Parameter | Type | Default value |
|---|---|---|
dag | DependencyGraph | undefined |
lineCache | LineCache | undefined |
vm | VM | undefined |
highWaterMark | number | AsyncResolutionBatcher.DEFAULT_HIGH_WATER_MARK |
Returns
Section titled “Returns”AsyncResolutionBatcher
Properties
Section titled “Properties”_testCaptures
Section titled “_testCaptures”_testCaptures: | AsyncResolutionEvent[] | null = null;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:123
Test-only synchronous capture array. When enabled (non-null), every event is synchronously pushed here in addition to the stream. Tests read from this array to avoid async stream reader timing issues.
checkpointer
Section titled “checkpointer”checkpointer: VMCheckpointer | null = null;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:168
The checkpoint chain to rebuild variable state from, when there is one.
A re-run reads whatever the VM currently holds, which after a full pass is
the state at the END of the document. That is the right answer only for a
name written once. :x = <fetched> above :x = 99 left the fetched value
standing at every line below the redefinition, because the line that
redefines it was not itself affected and so was not re-run: a line reading
x below it answered with the value from the top of the document.
With a chain, the batch is run as a sweep through the document instead: restore to the line before the first affected one, then walk forward, executing the affected lines and applying the recorded bindings of every writing line passed on the way. Null leaves the previous behaviour exactly as it was, which is what a host driving the batcher on its own gets.
isAwaited
Section titled “isAwaited”isAwaited: (() => boolean) | null = null;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:256
Answers whether someone is waiting on the engine’s settle(), set by the
owning engine. A caller awaiting settle() re-evaluates once it resolves,
which is receiving the result, so the unwired warning does not apply to it.
Null reads as nobody waiting.
keepResult
Section titled “keepResult”keepResult: | ((lineNumber, value, writeVariable) => Value) | null = null;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:150
Bounds what a re-run keeps, set by the owning engine: the answer as it
is, or the refusal when keeping it would take the note past
vm.maxRetainedElements (#694), with the name the line assigned let
go. Null keeps every answer.
onLineResult
Section titled “onLineResult”onLineResult: ((lineNumber, value) => void) | null = null;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:142
Called for each line whose result is patched after an async resolution, on both the main-thread and worker-pool paths.
A host that displays async results must set this. It is the only mechanism that moves a resolved value out of the LineCache and into the host’s own document state. The engine cannot do it itself: it does not own a document, the host does, and the batcher has no reference to one.
Nullable rather than a constructor parameter because it is cleared by
clearAll() and re-wired on re-subscribe, so it cannot be readonly. That
makes it easy to miss, which is why warnIfUnwired exists: leaving
it unset, with nobody reading getEventStream either, means async
values resolve into the cache and are never shown, with nothing to
indicate why. A host that genuinely does not want async results should
not register async resolvers at all.
queryClient
Section titled “queryClient”queryClient: QueryClient | null = null;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:186
The query cache of the engine that owns this batcher, handed to a re-run
line in its execution context (LineExecutionContext.queryClient).
A package’s plugin function reads a resolved value back from that context. It used to read one module-level slot the engine set when it ran a line, and a re-run here that left the slot as it was read whichever cache was published last: for an engine whose first line fetches, none at all, and with a second engine in the process that engine’s cache, so the other engine’s price was reported as this line’s answer (#710).
Set by the engine at construction. Null falls back to the cache on the VM’s own context, which is null for a host driving the batcher on its own with no cache of its own.
Accessors
Section titled “Accessors”dedupCount
Section titled “dedupCount”Get Signature
Section titled “Get Signature”get dedupCount(): number;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:396
Number of resolutions this tick collapsed into one already pending for the same (packageId, queryKey). Counted as they are refused: the batch itself never holds a repeat, so recounting it here always gave zero.
Returns
Section titled “Returns”number
listenerCount
Section titled “listenerCount”Get Signature
Section titled “Get Signature”get listenerCount(): number;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:405
Whether the internal event stream currently has an active reader.
1 if a consumer has called getEventStream().getReader() (or
otherwise locked the stream) and not released it, 0 otherwise.
Returns
Section titled “Returns”number
pendingCount
Section titled “pendingCount”Get Signature
Section titled “Get Signature”get pendingCount(): number;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:387
Number of resolutions currently queued for the next flush.
Returns
Section titled “Returns”number
workerOffloadCount
Section titled “workerOffloadCount”Get Signature
Section titled “Get Signature”get workerOffloadCount(): number;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:421
Number of flushes dispatched to a worker pool: always 0.
A re-run of more than fifty lines used to go to the internal execution
pool, whose workers run each line in a bare VM with no variables, no
packages and no line context, so price * qty came back as “Undefined
variable: price” once a refresh touched enough lines (#661). Every re-run
is on the main thread now. Kept so the Workers diagnostic tab and
BatcherMetrics keep their shape.
Returns
Section titled “Returns”number
Methods
Section titled “Methods”add(entry): void;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:328
Add a resolved query key to the pending batch.
Called by ExpressionEngine.resolveAsync() after a promise resolves or errors. If this is the first entry in the current tick, schedules a microtask flush.
Parameters
Section titled “Parameters”| Parameter | Type |
|---|---|
entry | BatchEntry |
Returns
Section titled “Returns”void
clearAll()
Section titled “clearAll()”clearAll(): void;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:426
Remove all listeners and cancel pending batch. Called on engine clear.
Returns
Section titled “Returns”void
getEventStream()
Section titled “getEventStream()”getEventStream(): ReadableStream<AsyncResolutionEvent>;Defined in: packages/engine/src/engine/AsyncResolutionBatcher.ts:382
Get the native event stream for stream-based consumers.
Use this for backpressure, cancellation, or the ability
to pipeTo() / pipeThrough() the event flow.
Returns
Section titled “Returns”ReadableStream<AsyncResolutionEvent>
A ReadableStream that emits AsyncResolutionEvent items as the batcher processes async resolutions.