##// END OF EJS Templates
Added safe.debounce function, this is a preview version
cin -
r159:130129fdbd20 default
parent child
Show More
@@ -1260,9 +1260,9
1260 }
1260 }
1261 },
1261 },
1262 "typescript": {
1262 "typescript": {
1263 "version": "3.6.4",
1263 "version": "4.1.5",
1264 "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.4.tgz",
1264 "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz",
1265 "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==",
1265 "integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==",
1266 "dev": true
1266 "dev": true
1267 },
1267 },
1268 "uri-js": {
1268 "uri-js": {
@@ -27,7 +27,7
27 "dojo-typings": "^1.11.9",
27 "dojo-typings": "^1.11.9",
28 "requirejs": "latest",
28 "requirejs": "latest",
29 "tape": "~4.11.0",
29 "tape": "~4.11.0",
30 "typescript": "~3.6.4",
30 "typescript": "~4.1.5",
31 "eslint": "6.1.0",
31 "eslint": "6.1.0",
32 "tslint": "5.18.0"
32 "tslint": "5.18.0"
33 }
33 }
@@ -5,6 +5,8 const _oid = typeof Symbol === "function
5 Symbol("__implab__oid__") :
5 Symbol("__implab__oid__") :
6 "__implab__oid__";
6 "__implab__oid__";
7
7
8 function _noop() { }
9
8 export function oid(instance: null | undefined): undefined;
10 export function oid(instance: null | undefined): undefined;
9 export function oid(instance: NonNullable<any>): string;
11 export function oid(instance: NonNullable<any>): string;
10 export function oid(instance: any): string | undefined {
12 export function oid(instance: any): string | undefined {
@@ -288,9 +290,14 export function delegate(target: any, _m
288 };
290 };
289 }
291 }
290
292
293 /** Returns promise which will be resolved after the specified amount of time.
294 *
295 * @param timeMs The delay before the promise will be resolved in milliseconds.
296 * @param ct Optional. A cancellation token for the operation.
297 */
291 export function delay(timeMs: number, ct = cancellationNone) {
298 export function delay(timeMs: number, ct = cancellationNone) {
292 ct.throwIfRequested();
299 ct.throwIfRequested();
293 return new Promise((resolve, reject) => {
300 return new Promise<void>((resolve, reject) => {
294 const h = ct.register(e => {
301 const h = ct.register(e => {
295 clearTimeout(id);
302 clearTimeout(id);
296 reject(e);
303 reject(e);
@@ -304,6 +311,53 export function delay(timeMs: number, ct
304 });
311 });
305 }
312 }
306
313
314 export function debounce<T extends any[], R, This>(func: (this: This, ...args: T) => R | PromiseLike<R>, wait: number) {
315 let cancel: (e?: any) => void = _noop;
316
317 const fn = function executedFunction(this: This, ...args: T) {
318 return new Promise<R>((resolve, reject) => {
319
320 // used to cleanup currently allocated resources
321 const _cleanup = () => {
322 cancel = _noop;
323 clearTimeout(handle);
324 };
325
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 });
345 };
346
347 fn.cancel = (e?: any) => cancel(e);
348
349 fn.applyAsync = async (thisArg: This, args: T, ct: ICancellation) => {
350 const h = ct.register(cancel);
351 try {
352 await fn.apply(thisArg, args);
353 } finally {
354 h.destroy();
355 }
356 };
357
358 return fn;
359 }
360
307 /** Returns resolved promise, awaiting this method will cause the asynchronous
361 /** Returns resolved promise, awaiting this method will cause the asynchronous
308 * completion of the rest of the code.
362 * completion of the rest of the code.
309 */
363 */
@@ -1,5 +1,6
1 import { Cancellation } from "../Cancellation";
1 import { Cancellation } from "../Cancellation";
2 import { first, isPromise, firstWhere, delay, nowait, notImplemented } from "../safe";
2 import { ICancellation } from "../interfaces";
3 import { first, isPromise, firstWhere, delay, nowait, notImplemented, debounce, fork } from "../safe";
3 import { test } from "./TestTraits";
4 import { test } from "./TestTraits";
4
5
5 test("await delay test", async t => {
6 test("await delay test", async t => {
@@ -93,3 +94,63 test("sequemce test", async t => {
93 v = await new Promise(resolve => first(asyncSequence, resolve));
94 v = await new Promise(resolve => first(asyncSequence, resolve));
94 t.equal(v, "a", "The callback should be called for the first element");
95 t.equal(v, "a", "The callback should be called for the first element");
95 });
96 });
97
98 test("debounce tests", async (t, trace) => {
99 let count = 0;
100 let rejected = 0;
101 function increment(step: number = 1) {
102 count += step;
103 return count;
104 }
105
106 const f = debounce(increment, 100);
107 f().then(undefined, () => rejected++);
108 f().then(undefined, () => rejected++);
109
110 await f(1);
111
112 t.equal(rejected, 2, "Previous operations should be rejected");
113 t.equal(count, 1, "The operation should run once");
114
115 const acc = debounce(
116 (...values: number[]) => count = values.reduce((a, v) => v + a, count),
117 100
118 );
119
120 acc(1, 2, 3).catch(() => { });
121 const result = acc(1, 2, 3);
122 acc.cancel();
123
124 try {
125 await result;
126 t.notOk("fn.cancel() should make current operation to throw an exception");
127 } catch {
128 t.ok("fn.cancel() should make current operation to throw an exception");
129 }
130
131 t.equal(count, 1, "fn.cancel() The operation should not run");
132
133 acc.cancel();
134 await acc(1, 2);
135 t.equal(count, 4, "The variable arguments list shoud be handled correctly");
136
137 // create cancellation token
138 let cancel: (e?: any) => void = notImplemented;
139 const ct = new Cancellation(c => cancel = c);
140
141 const d = debounce(async (ct2: ICancellation = Cancellation.none) => {
142 ct2.throwIfRequested();
143 trace.debug("do async increment");
144 await fork();
145 count++;
146 return count;
147 }, 0);
148
149 const p = d.applyAsync(null, [ct], ct).then(undefined, () => rejected++);
150 cancel();
151 await p;
152
153 t.equal(count, 4, "Cancellation token should prevent the function execution");
154 t.equal(rejected, 3, "Cancellation token should reject operation");
155
156 });
General Comments 0
You need to be logged in to leave comments. Login now