Context can be used in synchronous or asynchronous scenarios, the following example
illustrates both usages (code is simplified to only keep the relevant points):
classModuleextendsModuleFlux{output$ : Pipe<unknown>constructor(params) {super(params)addInput({ onTriggered: ({data, context}) =>this.process(data, context) })this.addOutput({id:'output'}) }process(data, context: Context) {letdata1 = context.withChild( 'syncStep', () =>syncStep(data, context) )ctx.info("Synchronous step was successful", data1)asyncStep(data1, context).subscribe( (result) => {this.output$.next(result);// it is always the responsibility of the developer to close// the context provided to 'onTriggered' context.end() }; (error) => { ctx.error( error )context.end() } }syncStep( data, context: Context){ context.info("Starting sync step", data) let value// = ... the result of some computations return value }asyncasyncStep( data: unknown, context: Context): Observable<any> { let ctx = context.startChild('async step') ctx.info("Starting async step, expect 10 values to come", data) return from(// a source of async data, e.g. requests, multi-threaded computations, etc ).pipe(tap( (value) =>ctx.info("got a value", value))take(10),reduce( (acc,e) =>acc.concat(e), []) tap( (acc) => { ctx.info("got all 10 values", acc) ctx.end() }) ) }}
The Context class provide the method withChild, the developer is not
in charge to start or end the child context as it is automatically managed.
If an error is thrown and not catched during the callback execution, the child context end (along with its parents),
the error is reported in the child context, and finally the error is re-thrown.
The Context class provides the method startChild & end.
It is the developer responsibility to call these methods at the right time, and also
to deal with eventual exceptions.
The natural representation of a context (as presented in flux-builder),
is a tree view representing the tree structure of the function calls chain.
The children of a context can be of two types:
The Context object also conveys the user-context (it is discussed along with Adaptor).
The library can not automatically forward the user-context when output are sent:
it is not always as meaningful as expected, and the asynchronous (possibly multi-threaded)
nature of module's processes makes it close to impossible (at least we did not find a safe way of doing so 🤫).
This explains why, when emitting a value through an output pipe, you should explicitly pass
back the context that has been provided to you at the first place (to the onTriggered callback).
Context
Context objects are used to track execution flows in the module's implementation (or at other places). It enables:
Context can be used in synchronous or asynchronous scenarios, the following example illustrates both usages (code is simplified to only keep the relevant points):
The natural representation of a context (as presented in flux-builder), is a tree view representing the tree structure of the function calls chain. The children of a context can be of two types:
Logs broadcasting
Context objects can includes broadcasting Context.channels$ to multiple destinations and with different filters/maps through LogChannel.
Context from modules' execution use 2 channels:
A note about user-context
The Context object also conveys the user-context (it is discussed along with Adaptor). The library can not automatically forward the user-context when output are sent: it is not always as meaningful as expected, and the asynchronous (possibly multi-threaded) nature of module's processes makes it close to impossible (at least we did not find a safe way of doing so 🤫).
This explains why, when emitting a value through an output pipe, you should explicitly pass back the context that has been provided to you at the first place (to the onTriggered callback).