| @@ -1,4 +1,5 | |||
|
|
1 | import { ICancellable, Constructor, IDestroyable, ICancellation, IRemovable } from "./interfaces"; | |
|
|
1 | import { Cancellation } from "./Cancellation"; | |
|
|
2 | import { ICancellable, Constructor, IDestroyable, ICancellation, IRemovable, PromiseOrValue } from "./interfaces"; | |
|
|
2 | 3 | |
|
|
3 | 4 | let _nextOid = 0; |
|
|
4 | 5 | const _oid = typeof Symbol === "function" ? |
| @@ -311,37 +312,36 export function delay(timeMs: number, ct | |||
|
|
311 | 312 | }); |
|
|
312 | 313 | } |
|
|
313 | 314 | |
|
|
314 | export function debounce<T extends any[], R, This>(func: (this: This, ...args: T) => R | PromiseLike<R>, wait: number) { | |
|
|
315 | /** | |
|
|
316 | * Wraps the specified function to delay its execution by the specified timeout. | |
|
|
317 | * If the wrapped function was called multiple times before the timeout has elapsed, | |
|
|
318 | * the target function will be executed only once. | |
|
|
319 | * Each call to the wrapped function will return a promise is the timeout hasn't | |
|
|
320 | * elapsed the subsequent calls to the wrapped function will reject previous promises. | |
|
|
321 | * | |
|
|
322 | * @param func function that accepts a cancellation token as a single | |
|
|
323 | * parameter and returns the callback which will be invoked. | |
|
|
324 | * @param wait A timeout to wait before the callback should be invoked | |
|
|
325 | * @returns The function withe the same parameters as the original callback. | |
|
|
326 | */ | |
|
|
327 | export function debounce<T extends any[], R, This>(func: (ct: ICancellation) => ((this: This, ...args: T) => PromiseOrValue<R>), wait: number) { | |
|
|
315 | 328 | let cancel: (e?: any) => void = _noop; |
|
|
316 | 329 | |
|
|
317 | const fn = function executedFunction(this: This, ...args: T) { | |
|
|
318 | return new Promise<R>((resolve, reject) => { | |
|
|
330 | let pending = Promise.resolve(); | |
|
|
319 | 331 | |
|
|
320 | // used to cleanup currently allocated resources | |
|
|
321 | const _cleanup = () => { | |
|
|
322 | cancel = _noop; | |
|
|
323 | clearTimeout(handle); | |
|
|
324 | }; | |
|
|
332 | const fn = async function executedFunction(this: This, ...args: T) { | |
|
|
333 | cancel(); | |
|
|
334 | const ct = new Cancellation(_cancel => cancel = _cancel); | |
|
|
325 | 335 | |
|
|
326 | // used in case of cancellation of the current operation | |
|
|
327 | const _cancel = (e: any) => { | |
|
|
328 | _cleanup(); | |
|
|
329 | reject(e); | |
|
|
330 | }; | |
|
|
331 | ||
|
|
332 | // performs actual work | |
|
|
333 |
|
|
|
|
334 | _cleanup(); | |
|
|
335 | resolve(func.apply(this, args)); | |
|
|
336 | }; | |
|
|
337 | ||
|
|
338 | // cancel previously queued operation | |
|
|
339 | if (cancel !== _noop) | |
|
|
340 | cancel(new Error("Operation cancelled due to debouncing")); | |
|
|
341 | cancel = _cancel; | |
|
|
342 | ||
|
|
343 | const handle = setTimeout(_later, wait); | |
|
|
344 | }); | |
|
|
336 | await pending; | |
|
|
337 | let resolve = _noop; | |
|
|
338 | pending = new Promise(_resolve => resolve = _resolve); | |
|
|
339 | try { | |
|
|
340 | await delay(wait, ct); | |
|
|
341 | return func(ct).apply(this, args); | |
|
|
342 | } finally { | |
|
|
343 | resolve(); | |
|
|
344 | } | |
|
|
345 | 345 | }; |
|
|
346 | 346 | |
|
|
347 | 347 | fn.cancel = (e?: any) => cancel(e); |
| @@ -109,17 +109,19 test("Load configuration from module", a | |||
|
|
109 | 109 | test("Optional dependency with child container", async t => { |
|
|
110 | 110 | const container = new Container<{ |
|
|
111 | 111 | foo?: Foo; |
|
|
112 |
box: Box< |
|
|
|
112 | box: Box<Bar>; | |
|
|
113 | bar: Bar; | |
|
|
113 | 114 | }>(); |
|
|
114 | 115 | await container.fluent({ |
|
|
115 |
box: it => it.factory($ => new Box($(" |
|
|
|
116 | box: it => it.factory($ => new Box($("bar"))), | |
|
|
117 | bar: it => it.factory($ => new Bar({ host: "local", foo: $("foo") }, "bar")) | |
|
|
116 | 118 | }); |
|
|
117 | 119 | |
|
|
118 | 120 | const child = await container.createChildContainer() |
|
|
119 | 121 | .fluent({ |
|
|
120 | 122 | foo: it => it.factory(() => new Foo()) |
|
|
121 | }) | |
|
|
123 | }); | |
|
|
122 | 124 | |
|
|
123 | 125 | const box = child.resolve("box"); |
|
|
124 | 126 | t.assert(!isNull(box.getValue()), "'foo' dependency is declared in child container"); |
|
|
125 | }); No newline at end of file | |
|
|
127 | }); | |
| @@ -103,7 +103,7 test("debounce tests", async (t, trace) | |||
|
|
103 | 103 | return count; |
|
|
104 | 104 | } |
|
|
105 | 105 | |
|
|
106 | const f = debounce(increment, 100); | |
|
|
106 | const f = debounce(() => increment, 100); | |
|
|
107 | 107 | f().then(undefined, () => rejected++); |
|
|
108 | 108 | f().then(undefined, () => rejected++); |
|
|
109 | 109 | |
| @@ -113,7 +113,7 test("debounce tests", async (t, trace) | |||
|
|
113 | 113 | t.equal(count, 1, "The operation should run once"); |
|
|
114 | 114 | |
|
|
115 | 115 | const acc = debounce( |
|
|
116 | (...values: number[]) => count = values.reduce((a, v) => v + a, count), | |
|
|
116 | () => (...values: number[]) => count = values.reduce((a, v) => v + a, count), | |
|
|
117 | 117 | 100 |
|
|
118 | 118 | ); |
|
|
119 | 119 | |
| @@ -138,15 +138,15 test("debounce tests", async (t, trace) | |||
|
|
138 | 138 | let cancel: (e?: any) => void = notImplemented; |
|
|
139 | 139 | const ct = new Cancellation(c => cancel = c); |
|
|
140 | 140 | |
|
|
141 | const d = debounce(async () => { | |
|
|
142 | ct.throwIfRequested(); | |
|
|
141 | const d = debounce(() => async (_ct: ICancellation) => { | |
|
|
142 | _ct.throwIfRequested(); | |
|
|
143 | 143 | trace.debug("do async increment"); |
|
|
144 | 144 | await fork(); |
|
|
145 | 145 | count++; |
|
|
146 | 146 | return count; |
|
|
147 | 147 | }, 0); |
|
|
148 | 148 | |
|
|
149 | const p = d().then(undefined, () => rejected++); | |
|
|
149 | const p = d(ct).then(undefined, () => rejected++); | |
|
|
150 | 150 | cancel(); |
|
|
151 | 151 | await p; |
|
|
152 | 152 | |
General Comments 0
You need to be logged in to leave comments.
Login now
