solve method
Starts all given descriptors
respecting dependency constraints.
The cyclic service resolution is carried out by performing following
checks on each unsolved service until either no unsolved services are left
or no new changes can be made:
-
Is this service optional and is it still unsolvable?
true => Skip the service and remove it from the list -
Is this still missing required dependencies or are missing optional dependencies still possibly obtainable from other unsolved services?
true => Skip this resolution cycle -
(indirect) Are all required dependencies provided?
true => Start the service
false => Throw an exception
In this algorithm, step 3 is indirectly performed as the resolution cycle will be skipped indefinitely in the case of an missing required dependency, which will result in the cancellation of the resolution loop since no further changes have been made in the last cycle.
Implementation
Stream<ResolveEvent> solve(List<ServiceDescriptor> descriptors) async* {
yield ResolveEvent(ResolveEventType.started, null);
var unsolved = descriptors.toList();
while (true) {
var lenBefore = unsolved.length; // Remember length before cycle
// Collect injector keys which could possibly become available
var futurePromises =
unsolved.expand((element) => element.publications).toList();
for (var descriptor in unsolved.toList()) {
// If the service is optional and not solvable in this context, skip it
if (descriptor.optional &&
!descriptor.isSolvable(injector, futurePromises)) {
unsolved.remove(descriptor);
yield ResolveEvent(
ResolveEventType.serviceSkipped,
descriptor,
descriptor.dependencies
.where((element) => !injector.checkKey(element))
.toList());
continue;
}
// If the service has unfulfilled dependencies or optional dependencies
// could still become available, wait another dependency cycle
if (descriptor.skipDependencyCycle(injector, futurePromises)) {
continue;
}
// All dependencies are met and no more optional dependencies can
// be fulfilled anymore -> start the service now.
unsolved.remove(descriptor);
await activate(descriptor);
yield ResolveEvent(ResolveEventType.serviceStarted, descriptor);
}
var lenAfter = unsolved.length;
if (lenAfter == 0) {
yield ResolveEvent(ResolveEventType.finished, null);
break;
}
if (lenBefore != lenAfter) continue; // At least one declined
for (var descriptor in unsolved) {
yield ResolveEvent(
ResolveEventType.serviceError,
descriptor,
descriptor.dependencies
.where((element) => !injector.checkKey(element))
.toList());
}
yield ResolveEvent(ResolveEventType.failed, null);
break;
}
}