Skip to content

Commit

Permalink
Merge pull request #101 from Techn1x/add-debounce-initialize
Browse files Browse the repository at this point in the history
Add debounce initialize
  • Loading branch information
NullVoxPopuli authored May 24, 2024
2 parents 6bcf17d + 79abaab commit 492abf2
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 13 deletions.
38 changes: 25 additions & 13 deletions reactiveweb/src/debounce.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,9 @@
import { tracked } from '@glimmer/tracking';

import { resource } from 'ember-resources';

class TrackedValue<T> {
@tracked value: T | undefined;
}
import { cell, resource } from 'ember-resources';

/**
* A utility for debouncing high-frequency updates.
* The returned value will only be updated every `ms` and is
* initially undefined.
* initially undefined, unless an initialize value is provided.
*
* This can be useful when a user's typing is updating a tracked
* property and you want to derive data less frequently than on
Expand Down Expand Up @@ -55,12 +49,30 @@ class TrackedValue<T> {
* }
* ```
*
* @example
* An initialize value can be provided as the starting value instead of it initially returning undefined.
* ```js
* import Component from '@glimmer/component';
* import { tracked } from '@glimmer/tracking';
* import { use } from 'ember-resources';
* import { debounce } from 'reactiveweb/debounce';
*
* const delay = 100; // ms
*
* class Demo extends Component {
* @tracked userInput = 'products';
*
* @use debouncedInput = debounce(delay, () => this.userInput, this.userInput);
* }
* ```
*
* @param {number} ms delay in milliseconds to wait before updating the returned value
* @param {() => Value} thunk function that returns the value to debounce
* @param {Value} initialize value to return initially before any debounced updates
*/
export function debounce<Value = unknown>(ms: number, thunk: () => Value) {
let lastValue: Value;
let state = new TrackedValue<Value>();
export function debounce<Value = unknown>(ms: number, thunk: () => Value, initialize?: Value) {
let lastValue: Value | undefined = initialize;
let state = cell<Value | undefined>(lastValue);

return resource(({ on }) => {
let timer: number;
Expand All @@ -74,9 +86,9 @@ export function debounce<Value = unknown>(ms: number, thunk: () => Value) {
});

timer = setTimeout(() => {
state.value = lastValue;
state.current = lastValue;
}, ms);

return () => state.value;
return () => state.current;
});
}
26 changes: 26 additions & 0 deletions tests/test-app/tests/utils/debounce/js-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,31 @@ module('Utils | debounce | js', function (hooks) {
`Value is "${test.debouncedValue}" after ~100ms`
);
});

test('initialize value', async function (assert) {
class Test {
@tracked value = 'initial';

@use debouncedValue = debounce(100, () => this.value, this.value);
}

let test = new Test();

setOwner(test, this.owner);

assert.strictEqual(test.debouncedValue, 'initial', 'Value is as given at first');

test.value = 'new';

assert.notEqual(test.debouncedValue, test.value, 'Value and debounced value have diverged');

await timeout(50);

assert.strictEqual(test.debouncedValue, 'initial', 'Value is still initial after ~50ms');

await timeout(50);

assert.strictEqual(test.debouncedValue, 'new', `Value is updated after ~100ms`);
});
});
});

0 comments on commit 492abf2

Please sign in to comment.