| @@ -0,0 +1,12 | |||
|
|
1 | import { Cancellation } from "./Cancellation"; | |
|
|
2 | import { ICancellation } from "./interfaces"; | |
|
|
3 | ||
|
|
4 | export class CancelledError extends Error { | |
|
|
5 | readonly cancellationToken: ICancellation; | |
|
|
6 | ||
|
|
7 | constructor(message = "The operation is cancelled", ct = Cancellation.none) { | |
|
|
8 | super(message); | |
|
|
9 | this.cancellationToken = ct; | |
|
|
10 | this.name = "CancelledError"; | |
|
|
11 | } | |
|
|
12 | } | |
| @@ -1,4 +1,5 | |||
|
|
1 | 1 | import { CancellationAggregate } from "./CancellationAggregate"; |
|
|
2 | import { CancelledError } from "./CancelledError"; | |
|
|
2 | 3 | import { ICancellation, IDestroyable } from "./interfaces"; |
|
|
3 | 4 | import { argumentNotNull, destroyed } from "./safe"; |
|
|
4 | 5 | |
| @@ -57,7 +58,7 export class Cancellation implements ICa | |||
|
|
57 | 58 | if (this._reason) |
|
|
58 | 59 | return; |
|
|
59 | 60 | |
|
|
60 |
this._reason = (reason = reason || new Error( |
|
|
|
61 | this._reason = (reason = reason || new CancelledError(undefined, this)); | |
|
|
61 | 62 | |
|
|
62 | 63 | if (this._cbs) { |
|
|
63 | 64 | this._cbs.forEach(cb => cb(reason)); |
| @@ -84,23 +85,23 export class Cancellation implements ICa | |||
|
|
84 | 85 | |
|
|
85 | 86 | /** |
|
|
86 | 87 | * Combines multiple cancellation tokens to the single aggregated token. |
|
|
87 |
* |
|
|
|
88 | * | |
|
|
88 | 89 | * Aggregated token will be considered as signalled when some tokens are |
|
|
89 | 90 | * signalled. The cancellation callback can be registered with the `register` |
|
|
90 | 91 | * method, it will be fired once with the first signalled token, all other |
|
|
91 | 92 | * tokens will be ignored. |
|
|
92 |
* |
|
|
|
93 | * | |
|
|
93 | 94 | * The tokens which don't support cancellation are filtered out, if there are |
|
|
94 | 95 | * no tokens left in the list the method returns `Cancellation.none`. |
|
|
95 |
* |
|
|
|
96 | * | |
|
|
96 | 97 | * @param args The list of cancellation tokens to combine |
|
|
97 | * @returns | |
|
|
98 | * @returns Aggregated cancellation token | |
|
|
98 | 99 | */ |
|
|
99 | 100 | static combine(...args: ICancellation[]) { |
|
|
100 | 101 | const tokens = args.filter(ct => ct.isSupported()); |
|
|
101 | 102 | return tokens.length > 1 ? |
|
|
102 | 103 | new CancellationAggregate(tokens) : |
|
|
103 | tokens.length == 1 ? tokens[0] : | |
|
|
104 | tokens.length === 1 ? tokens[0] : | |
|
|
104 | 105 | this.none; |
|
|
105 | 106 | } |
|
|
106 | 107 | } |
| @@ -26,14 +26,14 export class CancellationAggregate imple | |||
|
|
26 | 26 | destroy(); |
|
|
27 | 27 | cb(e); |
|
|
28 | 28 | } |
|
|
29 | } | |
|
|
29 | }; | |
|
|
30 | 30 | |
|
|
31 | 31 | const destroy = () => subscriptions |
|
|
32 | .splice(0,subscriptions.length) // empty array | |
|
|
32 | .splice(0, subscriptions.length) // empty array | |
|
|
33 | 33 | .forEach(subscription => subscription.destroy()); // cleanup |
|
|
34 | 34 | |
|
|
35 | const subscriptions = this._tokens.map(ct => ct.register(once)) | |
|
|
36 | ||
|
|
35 | const subscriptions = this._tokens.map(ct => ct.register(once)); | |
|
|
36 | ||
|
|
37 | 37 | return { |
|
|
38 | 38 | destroy |
|
|
39 | 39 | }; |
| @@ -18,7 +18,10 export class AsyncComponent implements I | |||
|
|
18 | 18 | |
|
|
19 | 19 | const guard = async () => { |
|
|
20 | 20 | try { |
|
|
21 |
|
|
|
|
21 | const combined = Cancellation.combine(ct, inner); | |
|
|
22 | const result = await op(combined); | |
|
|
23 | combined.throwIfRequested(); | |
|
|
24 | return result; | |
|
|
22 | 25 | } finally { |
|
|
23 | 26 | // after the operation is complete we need to cleanup the |
|
|
24 | 27 | // resources |
| @@ -209,7 +209,7 export class LifetimeManager implements | |||
|
|
209 | 209 | _lifetime.store(item); |
|
|
210 | 210 | }, |
|
|
211 | 211 | toString() { |
|
|
212 | return `[object ContainerLifetime, has=${_lifetime.has()}]` | |
|
|
212 | return `[object ContainerLifetime, has=${_lifetime.has()}]`; | |
|
|
213 | 213 | } |
|
|
214 | 214 | }; |
|
|
215 | 215 | } |
| @@ -29,10 +29,48 export interface IRemovable { | |||
|
|
29 | 29 | remove(): void; |
|
|
30 | 30 | } |
|
|
31 | 31 | |
|
|
32 | /** | |
|
|
33 | * Interface for the cancellation token. Cancellation token is | |
|
|
34 | * a marker indicating that the cancellation was requested, it | |
|
|
35 | * is up to the operation to decide whether to interrupt or | |
|
|
36 | * to complete its execution. | |
|
|
37 | * | |
|
|
38 | * This interface defines several methods of interaction with | |
|
|
39 | * the cancellation i.e. either to poll its status or to react | |
|
|
40 | * through the callback. | |
|
|
41 | */ | |
|
|
32 | 42 | export interface ICancellation { |
|
|
43 | /** | |
|
|
44 | * Throws an exception if the cancellation has been requested, | |
|
|
45 | * otherwise does nothing. | |
|
|
46 | */ | |
|
|
33 | 47 | throwIfRequested(): void; |
|
|
48 | ||
|
|
49 | /** | |
|
|
50 | * Checks whether the cancellation is requested. | |
|
|
51 | * @returns true is the cancellation has been requested, | |
|
|
52 | * otherwise returns false. | |
|
|
53 | */ | |
|
|
34 | 54 | isRequested(): boolean; |
|
|
55 | ||
|
|
56 | /** | |
|
|
57 | * Checks the ability of the token to be signaled. | |
|
|
58 | * | |
|
|
59 | * @returns true if the token is able to request | |
|
|
60 | * the cancellation, false otherwise. | |
|
|
61 | */ | |
|
|
35 | 62 | isSupported(): boolean; |
|
|
63 | ||
|
|
64 | /** | |
|
|
65 | * Registers the callback to be called when the cancellation | |
|
|
66 | * is requested. | |
|
|
67 | * | |
|
|
68 | * @param cb The callback which receives the reason of the | |
|
|
69 | * cancellation. | |
|
|
70 | * @returns The subscription, after the operation is completed | |
|
|
71 | * it should unregister the callback to free resources by | |
|
|
72 | * calling the `destroy()` method of the subscription. | |
|
|
73 | */ | |
|
|
36 | 74 | register(cb: (e: any) => void): IDestroyable; |
|
|
37 | 75 | } |
|
|
38 | 76 | |
| @@ -34,7 +34,7 test("Nested async configuration", async | |||
|
|
34 | 34 | }); |
|
|
35 | 35 | |
|
|
36 | 36 | t.assert(container.resolve("box").getValue(), "The dependency should be set"); |
|
|
37 | t.equals(container.resolve("box").getValue(), container.resolve("box").getValue(), "The service should be activated once") | |
|
|
37 | t.equals(container.resolve("box").getValue(), container.resolve("box").getValue(), "The service should be activated once"); | |
|
|
38 | 38 | }); |
|
|
39 | 39 | |
|
|
40 | 40 | test("Bad fluent config", async t => { |
| @@ -71,7 +71,7 test("Container applyConfig", async t => | |||
|
|
71 | 71 | |
|
|
72 | 72 | test("Child container config", async t => { |
|
|
73 | 73 | const container = await new Container<{}>().applyConfig(import("../mock/config")); |
|
|
74 | ||
|
|
74 | ||
|
|
75 | 75 | const fooServices: ContainerConfiguration<FooServices> = (await import("../mock/config2")).default; |
|
|
76 | 76 | |
|
|
77 | 77 | const child = await fooServices.apply(container.createChildContainer()); |
General Comments 0
You need to be logged in to leave comments.
Login now
