##// 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 let _nextOid = 0;
4 let _nextOid = 0;
4 const _oid = typeof Symbol === "function" ?
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 let cancel: (e?: any) => void = _noop;
328 let cancel: (e?: any) => void = _noop;
316
329
317 const fn = function executedFunction(this: This, ...args: T) {
330 let pending = Promise.resolve();
318 return new Promise<R>((resolve, reject) => {
319
331
320 // used to cleanup currently allocated resources
332 const fn = async function executedFunction(this: This, ...args: T) {
321 const _cleanup = () => {
333 cancel();
322 cancel = _noop;
334 const ct = new Cancellation(_cancel => cancel = _cancel);
323 clearTimeout(handle);
324 };
325
335
326 // used in case of cancellation of the current operation
336 await pending;
327 const _cancel = (e: any) => {
337 let resolve = _noop;
328 _cleanup();
338 pending = new Promise(_resolve => resolve = _resolve);
329 reject(e);
339 try {
330 };
340 await delay(wait, ct);
331
341 return func(ct).apply(this, args);
332 // performs actual work
342 } finally {
333 const _later = () => {
343 resolve();
334 _cleanup();
344 }
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 });
345 };
345 };
346
346
347 fn.cancel = (e?: any) => cancel(e);
347 fn.cancel = (e?: any) => cancel(e);
@@ -109,17 +109,19 test("Load configuration from module", a
109 test("Optional dependency with child container", async t => {
109 test("Optional dependency with child container", async t => {
110 const container = new Container<{
110 const container = new Container<{
111 foo?: Foo;
111 foo?: Foo;
112 box: Box<Foo>;
112 box: Box<Bar>;
113 bar: Bar;
113 }>();
114 }>();
114 await container.fluent({
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 const child = await container.createChildContainer()
120 const child = await container.createChildContainer()
119 .fluent({
121 .fluent({
120 foo: it => it.factory(() => new Foo())
122 foo: it => it.factory(() => new Foo())
121 })
123 });
122
124
123 const box = child.resolve("box");
125 const box = child.resolve("box");
124 t.assert(!isNull(box.getValue()), "'foo' dependency is declared in child container");
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 return count;
103 return count;
104 }
104 }
105
105
106 const f = debounce(increment, 100);
106 const f = debounce(() => increment, 100);
107 f().then(undefined, () => rejected++);
107 f().then(undefined, () => rejected++);
108 f().then(undefined, () => rejected++);
108 f().then(undefined, () => rejected++);
109
109
@@ -113,7 +113,7 test("debounce tests", async (t, trace)
113 t.equal(count, 1, "The operation should run once");
113 t.equal(count, 1, "The operation should run once");
114
114
115 const acc = debounce(
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 100
117 100
118 );
118 );
119
119
@@ -138,15 +138,15 test("debounce tests", async (t, trace)
138 let cancel: (e?: any) => void = notImplemented;
138 let cancel: (e?: any) => void = notImplemented;
139 const ct = new Cancellation(c => cancel = c);
139 const ct = new Cancellation(c => cancel = c);
140
140
141 const d = debounce(async () => {
141 const d = debounce(() => async (_ct: ICancellation) => {
142 ct.throwIfRequested();
142 _ct.throwIfRequested();
143 trace.debug("do async increment");
143 trace.debug("do async increment");
144 await fork();
144 await fork();
145 count++;
145 count++;
146 return count;
146 return count;
147 }, 0);
147 }, 0);
148
148
149 const p = d().then(undefined, () => rejected++);
149 const p = d(ct).then(undefined, () => rejected++);
150 cancel();
150 cancel();
151 await p;
151 await p;
152
152
General Comments 0
You need to be logged in to leave comments. Login now