Files
flipper/desktop/flipper-server-core/src/utils/StateMachine.tsx
Andrey Goncharov 81d0057a8d Fix potential race conditions for starting/stopping server add-ons
Reviewed By: mweststrate

Differential Revision: D34301593

fbshipit-source-id: 2950de8a8567318cd3e87eff176657df5ba8fd1b
2022-02-28 03:50:34 -08:00

87 lines
2.0 KiB
TypeScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/
import {EventEmitter} from 'events';
export class StateMachine<TState extends string, TError extends TState> {
private _error?: Error;
private valueEmitter = new EventEmitter();
constructor(private _currentState: TState) {}
get error() {
return this._error;
}
get currentState() {
return this._currentState;
}
set<T extends TState>(
...[newState, error]: T extends TError ? [T, Error] : [T]
) {
this._currentState = newState as TState;
this._error = error;
this.valueEmitter.emit(newState as TState);
}
wait<T extends TState | TState[]>(state: T): Promise<void> {
return new Promise((resolve) => {
this.once(state, resolve);
});
}
once(state: TState | TState[], cb: () => void): () => void {
return this.subscribe(state, cb, {once: true});
}
on(state: TState | TState[], cb: () => void): () => void {
return this.subscribe(state, cb);
}
is(targetState: TState | TState[]) {
if (!Array.isArray(targetState)) {
targetState = [targetState];
}
return targetState.includes(this._currentState);
}
private subscribe(
state: TState | TState[],
cb: () => void,
{once}: {once?: boolean} = {},
): () => void {
const statesNormalized = Array.isArray(state) ? state : [state];
if (statesNormalized.includes(this._currentState)) {
cb();
return () => {};
}
let executed = false;
const wrappedCb = () => {
if (!executed) {
executed = true;
cb();
}
};
const fn = once ? 'once' : 'on';
statesNormalized.forEach((item) => {
this.valueEmitter[fn](item, wrappedCb);
});
return () => {
statesNormalized.forEach((item) => {
this.valueEmitter.off(item, wrappedCb);
});
};
}
}