| @@ -1260,9 +1260,9 | |||
|
|
1260 | 1260 | } |
|
|
1261 | 1261 | }, |
|
|
1262 | 1262 | "typescript": { |
|
|
1263 |
"version": " |
|
|
|
1264 |
"resolved": "https://registry.npmjs.org/typescript/-/typescript- |
|
|
|
1265 | "integrity": "sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==", | |
|
|
1263 | "version": "4.1.5", | |
|
|
1264 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.5.tgz", | |
|
|
1265 | "integrity": "sha512-6OSu9PTIzmn9TCDiovULTnET6BgXtDYL4Gg4szY+cGsc3JP1dQL8qvE8kShTRx1NIw4Q9IBHlwODjkjWEtMUyA==", | |
|
|
1266 | 1266 | "dev": true |
|
|
1267 | 1267 | }, |
|
|
1268 | 1268 | "uri-js": { |
| @@ -27,7 +27,7 | |||
|
|
27 | 27 | "dojo-typings": "^1.11.9", |
|
|
28 | 28 | "requirejs": "latest", |
|
|
29 | 29 | "tape": "~4.11.0", |
|
|
30 |
"typescript": "~ |
|
|
|
30 | "typescript": "~4.1.5", | |
|
|
31 | 31 | "eslint": "6.1.0", |
|
|
32 | 32 | "tslint": "5.18.0" |
|
|
33 | 33 | } |
| @@ -5,6 +5,8 const _oid = typeof Symbol === "function | |||
|
|
5 | 5 | Symbol("__implab__oid__") : |
|
|
6 | 6 | "__implab__oid__"; |
|
|
7 | 7 | |
|
|
8 | function _noop() { } | |
|
|
9 | ||
|
|
8 | 10 | export function oid(instance: null | undefined): undefined; |
|
|
9 | 11 | export function oid(instance: NonNullable<any>): string; |
|
|
10 | 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 | 298 | export function delay(timeMs: number, ct = cancellationNone) { |
|
|
292 | 299 | ct.throwIfRequested(); |
|
|
293 | return new Promise((resolve, reject) => { | |
|
|
300 | return new Promise<void>((resolve, reject) => { | |
|
|
294 | 301 | const h = ct.register(e => { |
|
|
295 | 302 | clearTimeout(id); |
|
|
296 | 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 | 361 | /** Returns resolved promise, awaiting this method will cause the asynchronous |
|
|
308 | 362 | * completion of the rest of the code. |
|
|
309 | 363 | */ |
| @@ -1,5 +1,6 | |||
|
|
1 | 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 | 4 | import { test } from "./TestTraits"; |
|
|
4 | 5 | |
|
|
5 | 6 | test("await delay test", async t => { |
| @@ -93,3 +94,63 test("sequemce test", async t => { | |||
|
|
93 | 94 | v = await new Promise(resolve => first(asyncSequence, resolve)); |
|
|
94 | 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
