Scenario
Conceptually, Scenario
is a sequence of events, that are described by a piece of promise-based code. It is designed to simplify state management and asynchronous control flow in your application.
Scenario may start instantly or may be triggered by promise-like objects.
Scenario may be infinite or finite, if expirationTrigger
argument or repeat
config property is specified.
In most cases triggered scenarios are used. Trigger can be:
- AwaiEvent -
scenario(state.events.change, callback)
- Promise -
scenario(promise, callback)
- callback will run once when promise is resolved - Promise factory -
scenario(() => Promise.race([...]), callback)
Properties
- config - resolved config
- events - record of AwaiEvent events
Strategies
- fork - runs scenarios in parallel
- cyclic - runs scenario only if previous one is completed
- once - run scenario only one time
Events
- started - emits
StartedEvent
on every scenario callback run - fulfilled - emits
FulfilledEvent
when scenario callback finished running - rejected - emitted when callback throws
- settled - emits
SettledEvent
when no more callbacks will be run
Diagrams
Fork scenario may be useful when you want to handle every event. Be careful of race conditions when using this strategy.
Cyclic scenario may be useful when you want to make sure that callback would not be called until previous one is finished. For example, if you want to ignore all the refresh button clicks, until previous refresh is finished.
Finite scenario may be useful when useful when you want scenario to run specific amount of times, or until some event is emitted/promise resolved.
For example, in Paint example, subscenario is listening to draw
event, until stopDraw
event is emitted.
Overloads
Instant scenario
scenario(callback, config?)
Default strategy: cyclic
Infinite scenario
scenario(trigger, callback, config?)
Default strategy: fork
Finite scenario
scenario(trigger, expirationTrigger, callback, config?)
Default strategy: fork
If a trigger is a plain promise, default strategy is once
.
Every scenario may become finite if repeat
config property is specified.
Finite scenarios are thennable, that means you can await their settlement. It helps to write declarative flows, as in Paint example.
Examples
The best place to see how scenarios may be used and combined is Safe counter or Awai Paint examples.
scenario(counter.events.changed, (value) => {
sessionStorage.setItem('counter', value);
});
// Trigger may be a function returning a promise.
// This makes it possible to combine multiple promise-likes.
const dataRevalidateScenario = scenario(
() => Promise.any([delay(REFETCH_INTERVAL), refresh.events.invoked]),
async () => {
const data = await fetchData();
dataState.set(data);
},
{ strategy: 'cyclic' }
);
scenario(dataRevalidateScenario.events.fulfilled, () => {
console.log('Data revalidated')
});
scenario(
() => new Promise(resolve => setTimeout(resolve, 1000)),
() => {
console.log('Hello Awai')
},
{ repeat: 3 },
);
Types types
type Trigger<T> = PromiseLike<T> | (() => PromiseLike<T>);
type Callback<T, R> = (value: T) => R;
interface Config {
id: string;
repeat?: number;
strategy: 'fork' | 'cyclic' | 'once';
tags: [];
}
type ShouldExpirePredicate = () => boolean;
type ExpirationTrigger<T> = AwaiEvent<T> | PromiseLike<T> | ShouldExpirePredicate;
export interface FulfilledEvent<T, R> {
event: T;
result: R;
config: Config;
}
export interface SettledEvent<T> {
event?: T;
config: Config;
}
export interface StartedEvent<T> {
event: T;
config: Config;
}