浏览代码

Add `debounce()` function.

Afshin T. Darian 6 年之前
父节点
当前提交
6a29b562f4
共有 1 个文件被更改,包括 39 次插入2 次删除
  1. 39 2
      packages/coreutils/src/poll.ts

+ 39 - 2
packages/coreutils/src/poll.ts

@@ -234,11 +234,11 @@ export class Poll<T = any, U = any> implements IDisposable, IPoll<T, U> {
       throw new Error('Poll backoff growth factor must be at least 1');
     }
 
-    if (interval < 0 || interval > max) {
+    if ((interval < 0 || interval > max) && interval !== Private.NEVER) {
       throw new Error('Poll interval must be between 0 and max');
     }
 
-    if (max > Poll.MAX_INTERVAL) {
+    if (max > Poll.MAX_INTERVAL && max !== Private.NEVER) {
       throw new Error(`Max interval must be less than ${Poll.MAX_INTERVAL}`);
     }
 
@@ -544,6 +544,43 @@ export namespace Poll {
   export const MAX_INTERVAL = 2147483647;
 }
 
+class Debouncer extends Poll<void, void> {
+  constructor(factory: Poll.Factory<void, void>, public interval: number) {
+    super({ factory, name: 'DEBOUNCER' });
+    void super.stop();
+  }
+
+  readonly frequency: IPoll.Frequency = {
+    backoff: false,
+    interval: Infinity,
+    max: Infinity
+  };
+
+  readonly standby = 'when-hidden';
+
+  async debounce(): Promise<void> {
+    await this.schedule({
+      cancel: last => last.phase === 'refreshed',
+      interval: this.interval,
+      phase: 'refreshed'
+    });
+    await this.tick;
+  }
+}
+
+/**
+ * Returns a debounced function that can be called multiple times safely and
+ * only executes the underlying function once per `interval`.
+ *
+ * @param fn - The function to debounce.
+ *
+ * @param interval - The debounce interval; defaults to 500ms.
+ */
+export function debounce(fn: () => any, interval = 500): () => Promise<void> {
+  const debouncer = new Debouncer(async () => fn(), interval);
+  return () => debouncer.debounce();
+}
+
 /**
  * A namespace for private module data.
  */