inhibitOnNull
Return a step that yields the same values as the passed in step, but downstream
work is inhibited when a yielded value is null or undefined.
The inhibition only affects steps that depend on the wrapper step (and those that depend on those, and so on); steps depending on the original (unwrapped) step are not otherwise impacted.
For those curious only, you don't need to understand this.
The execute method of a step class is passed
the batched executionValue (EV) for each step it depends on to provide the
data needed for execution.
If there is any inhibition, the step will instead be passed a derived set of new EVs which have the indices (indexes) that are inhibited in any EV removed from all EVs - i.e. those specific indices are "skipped" and the "batch size" is adjusted downwards to match. Once the step has executed, the result has these indices re-populated (with an inhibited value) such that the batch size remains consistent with other steps in the same layer plan.
Should all indices be inhibited, the step will not execute at all.
Example
const $parentId = get($post, "parentId");
// If $parentId is null, the `loadOne` will be inhibited (will not execute)
const $parent = loadOne(inhibitOnNull($parentId), batchGetPostById);
In the example above, when the $parentId step yields null, the associated
value for the loadOne step will automatically yield null (and be inhibited)
without needing to execute. Only the uninhibited non-null values pass through to
the loadOne step for execution. If all values for a step are inhibited then the
step will not execute at all. When a value for $parentId is not nullish,
execution for that value proceeds as normal.
Trapping
Combine inhibitOnNull with trap if you want to convert an
inhibited value back into a regular null (or another value) further down the
plan.
Plan diagrams
Internally, inhibitOnNull currently creates a __FlagStep, but that step is
usually converted into a dependency constraint when another step depends on it.
Thus in plan diagrams it's more common to see it on the dependency edge:
instead of a visible node, you will see the dependency arrow annotated with
labels such as rejectNull.
The label signals that downstream work will be inhibited if the value coming
from Access is null.
Advanced
inhibitOnNull accepts an optional condition step via { if: $cond } (see
condition for common conditions). When provided, the value is
only inhibited if both the wrapped step yields null and $cond yields a
truthy value:
// Decode `$id` assuming it is the Node `ID` for a user:
const spec = specFromNodeId(userHandler, $id);
// This will represent the value `null` if the ID is null or invalid
const $userIdOrNull = spec.id;
// If `$id` is `null`, we want a literal `null` here.
// However, if `$id` is non-null, but `$userIdOrNull` yields `null`
// (because, for example, an invalid ID or ID for a different type was passed),
// then we want to inhibit all proceeding steps.
const $validUserIdOrNull = inhibitOnNull($userIdOrNull, {
if: condition("not null", $id),
});