##// END OF EJS Templates
Rewritten safe::debounce
cin -
r163:4031b379ac68 v1.4.1 default
parent child
Show More
@@ -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 const _later = () => {
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<Foo>;
112 box: Box<Bar>;
113 bar: Bar;
113 114 }>();
114 115 await container.fluent({
115 box: it => it.factory($ => new Box($("foo")))
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