Opens an execution for a job, resolves the service lookups the job makes against the bindings it was created with,
and disposes the instances constructed along the way once the job settles.
Every invocation stands on its own, sharing no service instance with another. The promise handed back resolves to
the value the job produces, settling only after disposal has completed, and rejects if the executor is invoked from
within a running execution.
Bound implementations are constructed as the execution opens, in binding order, before the job is handed control:
an implementation may therefore await whatever it needs and still leave the job a ready instance on a synchronous
lookup, at the cost of being constructed even where the job never resolves it. An execution failing to prepare its
bindings rejects without running the job, disposing whatever it had already constructed.
Important
A construction reaching a bound service the pass has yet to prepare is abandoned there and run again from the
start once that one is ready, as a synchronous lookup is unable to wait. Unwinding takes every factory still
under construction, the bound implementation and any unbound default whose own body reached the pending binding,
so all of them must meet the all-or-nothing requirement stated at Service: an implementation acquiring a
resource or recording state as it awaits is the one this catches out. An unbound default that had already
completed is kept, and the repeat is handed the same instance. Ordering a binding after the ones it depends on
spares it the repeat; implementations requiring each other are rejected as circular.
Important
Services implementing AsyncDisposable or
Disposable are disposed in reverse construction
order, whether the job succeeded or failed, the asynchronous protocol taking precedence where both are implemented;
instances implementing neither are left as they are, which is not an error. Disposal runs outside the execution,
so a disposer resolving a service is rejected rather than constructing an instance nothing would dispose. Every
disposer is run even if an earlier one failed: if the job and a disposal both fail, or several disposals fail, the
errors are collected into an
AggregateError,
in the order they were raised.
Job executor.
Opens an execution for a job, resolves the service lookups the job makes against the bindings it was created with, and disposes the instances constructed along the way once the job settles.
Every invocation stands on its own, sharing no service instance with another. The promise handed back resolves to the value the job produces, settling only after disposal has completed, and rejects if the executor is invoked from within a running execution.
Bound implementations are constructed as the execution opens, in binding order, before the job is handed control: an implementation may therefore await whatever it needs and still leave the job a ready instance on a synchronous lookup, at the cost of being constructed even where the job never resolves it. An execution failing to prepare its bindings rejects without running the job, disposing whatever it had already constructed.
A construction reaching a bound service the pass has yet to prepare is abandoned there and run again from the start once that one is ready, as a synchronous lookup is unable to wait. Unwinding takes every factory still under construction, the bound implementation and any unbound default whose own body reached the pending binding, so all of them must meet the all-or-nothing requirement stated at Service: an implementation acquiring a resource or recording state as it awaits is the one this catches out. An unbound default that had already completed is kept, and the repeat is handed the same instance. Ordering a binding after the ones it depends on spares it the repeat; implementations requiring each other are rejected as circular.
Services implementing AsyncDisposable or Disposable are disposed in reverse construction order, whether the job succeeded or failed, the asynchronous protocol taking precedence where both are implemented; instances implementing neither are left as they are, which is not an error. Disposal runs outside the execution, so a disposer resolving a service is rejected rather than constructing an instance nothing would dispose. Every disposer is run even if an earlier one failed: if the job and a disposal both fail, or several disposals fail, the errors are collected into an AggregateError, in the order they were raised.