PSA: Wrapping an action with RunActions() will make sure that the doc recalculates between actions

I always thought the only purpose of RunActions() was to allow specifying multiple actions with a comma in a block (top-level or within a FormulaMap()), and it could be easily omitted if the action was just one action.

I.e. I thought these were identical:

Sequence(1, 5).FormulaMap(
Sequence(1, 5).FormulaMap(RunActions(

Turns out they’re not:

  • Without RunActions(), Coda executes the action(s) right away without recalculating the doc between actions. This is fast (maybe even parallel) but this also means that if anything changes as a result of the first action invocation in the loop, it won’t have any effect until the whole loop runs.
  • With RunActions(), Coda executes each action and waits for the doc to recalculate before running the next action. This is slower and may look like changes happening one by one in the doc, but this ensures that side effects of the previous action (such as a row added or modified) can be used as inputs for the next action.

See how AddRow(Table.Count()) works with or without RunActions(): (1)

It’s baffling that I only get to learn this after two years of expert consulting with Coda :astonished:. Or maybe the behavior wasn’t like this forever and changed as a result of recent work on non-blocking actions. I remember I was safely omitting RunActions and it always worked as I expected it to. @Himanshu could this be a recent change?

Either way thanks @Jason_Tamulonis for sharing this with me.


Thanks a lot for the useful insight @Paul_Danyliuk !

Unfortunately, I have no memories of different behaviours with or without RunActions().
I basically use it only when I need to - well - run multiple actions instead of a single one.

Perhaps this could be a good starting point of discussion in order to evolve and extend the concept with a Run(recalc:boolean, Actions[]) in order to have a specific - and conscious - control.

And maybe with a more comprehensive syntax, as expressed elsewhere for different needs:

Having conditional defaulted constructs, would help both writability and readability:

Run(true, AddRow(...)).When(condition)  // explicitly recalculate at each AddRow() if the condition is met. If not, _Noop() is implicit
When(condition, Run(true, AddRow(...)) // same with nesting syntax
Run(AddRow(...)).Unless(condition)  // Do not trigger the recalculation (by default) 
Unless(condition, Run(AddRow(...)) // same with nesting syntax