init method
Drives the service from ServiceState.NOT_INITIALIZED to ServiceState.RUN_SUCCESS by running every listener from provideInitListeners sequentially.
Contract:
- Calling init after dispose (any DISPOSE_* state) resolves to Err without re-running listeners. The service is terminal and cannot be re-initialized — callers must construct a fresh instance.
- Calling init a second time while the service is still RUN_/PAUSE_/ RESUME_* resolves to Err without re-running listeners (idempotent — listeners run exactly once per service lifetime).
- On the first valid call: listeners run; the service transitions to
ServiceState.RUN_ATTEMPT during execution and then
RUN_SUCCESS/RUN_ERRORdepending on outcome. IfeagerErroristrue(default), the chain short-circuits on the first listener error.
Returning Err on invalid transitions (instead of silent Ok in release or AssertionError in debug) is a mission-critical reliability choice: callers checking the awaited Result can detect "init was skipped because the service is in the wrong state" without relying on asserts being on.
Implementation
@nonVirtual
Resolvable<Unit> init({bool eagerError = true}) {
return _sequencer.then((prev) {
// Debug-only diagnostic: surface contract violations early so dev
// builds catch them before they manifest as silent Errs in prod.
assert(
!state.didDispose(),
'$runtimeType.init: cannot be called after dispose; state is $state. '
'Services are terminal after dispose — construct a fresh instance.',
);
if (state.didDispose()) {
return Sync<Option>.err(
Err('init: cannot be called after dispose; '
'state is $state.'),
);
}
assert(
state == ServiceState.NOT_INITIALIZED,
'$runtimeType.init: already initialized; state is $state. '
'init() runs listeners exactly once per service lifetime.',
);
if (state != ServiceState.NOT_INITIALIZED) {
return Sync<Option>.err(
Err('init: already initialized; state is $state. '
'init() runs listeners exactly once per service lifetime.'),
);
}
return _updateState(
providerFunction: provideInitListeners,
eagerError: eagerError,
attemptState: ServiceState.RUN_ATTEMPT,
successState: ServiceState.RUN_SUCCESS,
errorState: ServiceState.RUN_ERROR,
phaseName: 'init',
onSuccessMustNotThrow: Some(() {
_didEverInitAndSuccessfully = true;
}),
);
}).toUnit();
}