Skip to content

Activity Modes

Every activity you add with .do() runs in one of several modes. The mode controls how the operation executes — once, in a loop, conditionally, or in parallel. Modes are selected with the ACTIVITY enum:

import {ActionBuilder, ACTIVITY} from "@gesslar/actioneer"

The simplest mode runs an activity exactly once per context. Just pass a name and an operation:

class MyAction {
setup(builder) {
builder.do("processItem", ctx => {
ctx.result = ctx.input * 2
return ctx
})
}
}

Loops while a predicate returns true. The predicate is evaluated before each iteration, so the body may run zero times.

import {ActionBuilder, ACTIVITY} from "@gesslar/actioneer"
class CounterAction {
#shouldContinue = ctx => ctx.count < 10
#increment = ctx => { ctx.count += 1; return ctx }
setup(builder) {
builder
.do("initialize", ctx => { ctx.count = 0; return ctx })
.do("countUp", ACTIVITY.WHILE, this.#shouldContinue, this.#increment)
.do("finish", ctx => { return ctx.count })
}
}

Signature: .do(name, ACTIVITY.WHILE, predicate, operation)

Loops until a predicate returns true. The predicate is evaluated after each iteration, so the body always runs at least once.

import {ActionBuilder, ACTIVITY} from "@gesslar/actioneer"
class ProcessorAction {
#queueIsEmpty = ctx => ctx.queue.length === 0
#processItem = ctx => {
const item = ctx.queue.shift()
ctx.processed.push(item)
return ctx
}
setup(builder) {
builder
.do("initialize", ctx => {
ctx.queue = [1, 2, 3, 4, 5]
ctx.processed = []
return ctx
})
.do("process", ACTIVITY.UNTIL, this.#queueIsEmpty, this.#processItem)
.do("finish", ctx => { return ctx.processed })
}
}

Signature: .do(name, ACTIVITY.UNTIL, predicate, operation)

Conditionally runs an activity at most once. If the predicate is true, the operation runs once; if false, the activity is skipped entirely.

import {ActionBuilder, ACTIVITY} from "@gesslar/actioneer"
class ConditionalAction {
#shouldProcess = ctx => ctx.value > 10
#processLargeValue = ctx => { ctx.processed = ctx.value * 2; return ctx }
setup(builder) {
builder
.do("initialize", ctx => { ctx.value = 15; return ctx })
.do("maybeProcess", ACTIVITY.IF, this.#shouldProcess, this.#processLargeValue)
.do("finish", ctx => { return ctx })
}
}

Signature: .do(name, ACTIVITY.IF, predicate, operation)

BREAK and CONTINUE control loops from the inside, mirroring their JavaScript namesakes. Both must live inside a nested builder that is the body of a WHILE or UNTIL loop.

  • BREAK exits the enclosing loop when its predicate is true.
  • CONTINUE skips the rest of the current iteration when its predicate is true.
.do("loop", ACTIVITY.WHILE, ctx => ctx.count < 100,
new ActionBuilder()
.do("increment", ctx => { ctx.count++; return ctx })
.do("earlyExit", ACTIVITY.BREAK, ctx => ctx.count >= 5)
)

Signatures: .do(name, ACTIVITY.BREAK, predicate) and .do(name, ACTIVITY.CONTINUE, predicate)

These get their own deep dive in Control Flow.

Fans a context out into multiple parallel sub-runs, then rejoins the results. SPLIT needs a splitter (divides the context), a rejoiner (recombines results), and an operation (runs per split).

.do("parallel", ACTIVITY.SPLIT, splitter, rejoiner, operation)

Because parallel work and settled results have a few sharp edges, SPLIT has a dedicated guide: Parallelism with SPLIT.

ModeSignaturePredicate timingUse case
Default.do(name, operation)Execute once per context
WHILE.do(name, ACTIVITY.WHILE, predicate, operation)Before iterationLoop while condition is true
UNTIL.do(name, ACTIVITY.UNTIL, predicate, operation)After iterationLoop until condition is true
IF.do(name, ACTIVITY.IF, predicate, operation)Before executionRun once or skip
BREAK.do(name, ACTIVITY.BREAK, predicate)When reachedExit enclosing loop
CONTINUE.do(name, ACTIVITY.CONTINUE, predicate)When reachedSkip to next iteration
SPLIT.do(name, ACTIVITY.SPLIT, splitter, rejoiner, operation)Parallel split/rejoin
  • One mode per activity. Combining modes throws.
  • SPLIT requires both functions. The splitter and rejoiner are mandatory.
  • Predicates return booleans. All predicates (WHILE, UNTIL, IF, BREAK, CONTINUE) should return true or false.
  • BREAK/CONTINUE need a loop. They only work inside a nested builder that is the body of a WHILE or UNTIL activity.