Botmation Documentation
Loops
Loops
These BotActions provide functional loops.
These functions are both fun to play with and can reduce your overall code, while keeping it declarative and functional.
The assembled BotActions are ran in a Pipe
For All
It's a functional "forEach loop".
const forAll = (collection?: Collection) => (botActionOrActionsFactory: (...args: [any, string, Collection]) => BotAction<any>[] | BotAction<any>): BotAction<any> => async(page, ...injects) => { if (!collection) { collection = getInjectsPipeValue(injects) }
if (!collection) { logWarning('Loops forAll() missing collection') return }
let returnValue: AbortLineSignal|PipeValue
if (Array.isArray(collection)) { for(let index = 0; index < collection.length; index++) { injects = removePipe(injects) injects.push(wrapValueInPipe([collection[index], index+'', collection]))
returnValue = await pipeActionOrActions(botActionOrActionsFactory(collection[index], index+'', collection))(page, ...injects)
if (isAbortLineSignal(returnValue)) { return processAbortLineSignal(returnValue) } } } else { if (isDictionary(collection)) { for (const [key, value] of Object.entries(collection)) { injects = removePipe(injects) injects.push(wrapValueInPipe([value, collection, key]))
returnValue = await pipeActionOrActions(botActionOrActionsFactory(value, key, collection))(page, ...injects)
if (isAbortLineSignal(returnValue)) { return processAbortLineSignal(returnValue) } } } } }
This BotAction
takes a collection, either an array of any type or a simple json object with key/value pairs, to iterate with a callback function that returns a Bot Action or an array of Bot Action's to run in.
It's possible to pipe in the collection
as the first forAll()
call's parameter is optional.
The callback function is provided three params that are the collection's iterated value, the index casted as a string, and the collection itself. The index is normalized as a string to support the case of iterating an object's key->value pairs.
With each loop iteration, the pipe value injected into the initial assembled BotAction changes. It's created before each loop iteration line, with the same params as the callback, except wrapped in an array. The three values will be the collection's iterated value, the index the loop is iterating on, and the collection itself.
This function assembles a line of BotAction's with each loop iteration, it runs many BotAction lines. Therefore, it has advanced aborting behavior to give granular control. Here's a table to understand the effects AbortLineSignal's have with varying assembledLines
counts:
assembledLines | effect |
---|---|
1 | break the loop iteration line, but do not abort the loop |
2+ | break the loop iteration line, break the loop and return the AbortLineSignal with 2 assembledLines processed |
The screenshotAll() Bot Action wraps the forAll()()
Bot Action, to run goTo()
and screenshot()
actions, on a collection of urls provided.
For As Long
It's a functional "while loop".
const forAsLong = (condition: ConditionalBotAction) => (...actions: BotAction[]): BotAction<any> => async(page, ...injects) => { let returnValue: PipeValue|AbortLineSignal let resolvedCondition = await condition(page, ...pipeInjects(injects))
if (isAbortLineSignal(resolvedCondition)) { return processAbortLineSignal(resolvedCondition) }
while (resolvedCondition) { returnValue = await pipe()(...actions)(page, ...pipeInjects(injects))
if (isAbortLineSignal(returnValue)) { return processAbortLineSignal(returnValue) }
resolvedCondition = false resolvedCondition = await condition(page, ...pipeInjects(injects))
if (isAbortLineSignal(resolvedCondition)) { return processAbortLineSignal(resolvedCondition) } } }
It resolves the ConditionalBotAction before running the actions each time. It stops looping if the condition resolves False or rejects.
To support granular aborting, it takes two assembledLines
in an AbortLineSignal to fully abort out of forAsLong()()
. An AbortLineSignal with 1 assembledLines
will abort a loop iteration line of BotAction's, but not the loop itself. It's similar to forAll()(), except the ConditionalBotAction of the first forAsLong()
call can return an AbortLineSignal, which is handled normally. Therefore, if a ConditionalBotAction returns an AbortLineSignal with one assembledLines
, it aborts fully out of this BotAction.
For a concept example, see Loops: For As Long.
Do While
It's a functional "doWhile loop".
const const doWhile = (condition: ConditionalBotAction) => (...actions: BotAction<any>[]): BotAction<any> => async(page, ...injects) => { let returnValue: PipeValue|AbortLineSignal let resolvedCondition: boolean|AbortLineSignal = true while (resolvedCondition) { returnValue = await pipe()(...actions)(page, ...injects)
if (isAbortLineSignal(returnValue)) { return processAbortLineSignal(returnValue) }
resolvedCondition = false resolvedCondition = await condition(page, ...pipeInjects(injects))
if (isAbortLineSignal(resolvedCondition)) { return processAbortLineSignal(resolvedCondition) } } }
It runs the BotAction's first, then resolves the ConditionalBotAction as to whether or not it should run the actions again. It will keep running the pipe of Botaction's in a loop until the condition resolves false
or rejects.
This BotAction follows the same aborting behavior as forAsLong()(). The ConditionalBotAction can abort the function normally, while assembled BotAction's can abort a loop iteration line only, or that and abort the doWhile()()
BotAction.
For a concept example, see Loops: Do While.
Edit this page on GitHub