Browse Source

Switch back to a Poll sub-class to support both `throttle()` and `debounce()`.

Afshin T. Darian 6 years ago
parent
commit
015285bebd

+ 0 - 31
packages/coreutils/src/debounce.ts

@@ -1,31 +0,0 @@
-// Copyright (c) Jupyter Development Team.
-// Distributed under the terms of the Modified BSD License.
-
-import { PromiseDelegate } from '@phosphor/coreutils';
-
-/**
- * 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<T>(
-  fn: () => T | Promise<T>,
-  interval = 500
-): () => Promise<T> {
-  let delegate: PromiseDelegate<T> | null = null;
-
-  return () => {
-    if (delegate) {
-      return delegate.promise;
-    }
-    delegate = new PromiseDelegate<T>();
-    setTimeout(async () => {
-      delegate.resolve(await fn());
-      delegate = null;
-    }, interval);
-    return delegate.promise;
-  };
-}

+ 1 - 1
packages/coreutils/src/index.ts

@@ -3,13 +3,13 @@
 
 export * from './activitymonitor';
 export * from './dataconnector';
-export * from './debounce';
 export * from './interfaces';
 export * from './markdowncodeblocks';
 export * from './nbformat';
 export * from './pageconfig';
 export * from './path';
 export * from './poll';
+export * from './regulator';
 export * from './settingregistry';
 export * from './statedb';
 export * from './text';

+ 68 - 0
packages/coreutils/src/regulator.ts

@@ -0,0 +1,68 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import { Poll } from './poll';
+
+interface IRegulator {
+  invoke(): Promise<void>;
+  stop(): Promise<void>;
+}
+
+export type Debounced = IRegulator;
+
+export type Throttled = IRegulator;
+
+class Regulator extends Poll<void, void, 'invoked'> {
+  constructor(options: {
+    factory: Poll.Factory<void, void, 'invoked'>;
+    constraints: { interval: number; strategy: 'debounce' | 'throttle' };
+  }) {
+    super({ factory: options.factory });
+    this.constraints = options.constraints;
+    this.frequency = { backoff: false, interval: Poll.NEVER, max: Poll.NEVER };
+    this.standby = 'never';
+    void super.stop();
+  }
+
+  readonly constraints: { interval: number; strategy: 'debounce' | 'throttle' };
+
+  async invoke(): Promise<void> {
+    const { interval, strategy } = this.constraints;
+    if (strategy === 'debounce') {
+      return this.schedule({ interval, phase: 'invoked' });
+    }
+    if (this.state.phase !== 'invoked') {
+      return this.schedule({ interval, phase: 'invoked' });
+    }
+  }
+}
+
+/**
+ * Returns a debounced function that can be called multiple times and only
+ * executes the underlying function one `interval` after the last invocation.
+ *
+ * @param fn - The function to debounce.
+ *
+ * @param interval - The debounce interval; defaults to 500ms.
+ */
+export function debounce(fn: () => any, interval = 500): Debounced {
+  return new Regulator({
+    factory: async () => await fn(),
+    constraints: { interval, strategy: 'debounce' }
+  });
+}
+
+/**
+ * Returns a throttled function that can be called multiple times and only
+ * executes the underlying function once per `interval`.
+ *
+ * @param fn - The function to throttle.
+ *
+ * @param interval - The throttle interval; defaults to 500ms.
+ */
+export function throttle(fn: () => any, interval = 500): Throttled {
+  return new Regulator({
+    factory: async () => await fn(),
+    constraints: { interval, strategy: 'throttle' }
+  });
+}