npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

signature-debouncer

v1.1.1

Published

Debounce functions based on a custom signature

Readme

signature-debouncer

Debounces function invocations globally on the basis of a custom data signature

Installation

npm install signature-debouncer

or

yarn add signature-debouncer

API

debouncer.run(func: () => void, signature: Object, duration: number, options: DebounceOptions = {})

Will run func after duration ms, unless any calls to run with the same signature are also made inside this time.

If the duration has not timed out, and another call with the same signature is made, the function timer will reset (debounce) and will now be called after the new duration parameter.

Optional params can be included in the options object. Currently this includes:

  • fireNow: boolean
    • Setting this to true will ignore the duration and invoke the function on that signature immediately.

debouncer.cancel(signature: Object)

This will cancel any pending function invocation on the provided signature.

Note: signature equality is tested via JSON stringification,

Usage

The SignatureDebouncer (imported as debouncer) can be used anywhere in your project to globally debounce a function based on some arbitrary signature.

Basic Usage:

import debouncer from 'signature-debouncer';

const data = [1, 2, 3, 4];
const func = (value: number) => data.push(value);

// this signature is entirely arbitrary
const signature = "push to data array";

// debounce the function, providing the signature 
debouncer.run(() => func(5), signature, 1000);

// after say 500ms, if we call again with the same signature, it debounces the first call;
debouncer.run(() => func(8), signature, 1000);

// after 1000ms, as long as we don't call again on the same signature, the invocation will finally run

console.log(data);

// [1, 2, 3, 4, 8]

The takeaway is that the debounce is entirely based on the provided signature, regardless of the function supplied.

Reading the /tests directory may be helpful to observe more use cases and behaviour.

Using the Signature:

The signature can be any object you wish to debounce on the basis of. Common examples include the function's parameters, the function name - anything you possibly may want to use to differentiate what function calls are to be debounced independently.

For example, if I wanted a function to only debounce based on a custom signature, that signature could be an object containing it's name and arg.

I could first do the following to run it as a regular old timeout function:

const data = [1,2,3,4];
const func = (value: number) => data.push(value);

const arg = 5;
const signature = { funcName: func.name, arg };

debouncer.run(() => func(arg), signature);

// After 1000ms

console.log(someData);

// [1,2,3,4,5]

However if I did debounce.run on a different function with the same signature for whatever reason, before the other had timed out, the original function would be debounced and replaced with the new one!

const data = [1,2,3,4];
const func = (value: number) => data.push(value);

const arg = 5;
const anotherArg = 6;
const signature = { funcName: func.name, arg };

debouncer.run(() => func(arg), signature);
debouncer.run(() => func(anotherArg), signature);

// After 1000ms
// The original function call is debounced by the more recent one:

console.log(data);

// [1, 2, 3, 4, 6]

Alternatively, running the same function with a different signature, will have them running in parallel, since the debouncer will only debounce for the same signature!

const data = [1,2,3,4];
const func = (value: number) => data.push(value);

const arg = 5;
const signature = { funcName: func.name, arg }

const anotherArg = 6;
const anotherSignature = { funcName: func.name, anotherArg };

debouncer.run(() => exampleFunction(arg), signature);
debouncer.run(() => exampleFunction(anotherArg), anotherSignature);

// After 1000ms

console.log(someData);

// [1, 2, 3, 4, 5, 6]

More Practical Examples

For a more realistic use case, suppose you have some data with ID 1234 being updated from multiple sources in quick succession, but only want to apply the final result after the flurry of updates is over. You could simply use the id of the data 1234 as the signature and apply a delay of 5000 ms:

const item = {
  id: "1234",
  title: "hello world",
  date: "2024-04-24"
};

const updateTitle = (title) => {
  item.title = title;
}
const updateCats = (name) => {
  item.name = date;
}

// These calls could come from anywhere in the project...
debouncer.run(() => updateDate("2024-04-25"), item.id, 5000);
debouncer.run(() => updateTitle('test'), item.id, 5000);
debouncer.run(() => updateDate("2024-04-25"), item.id, 5000);
debouncer.run(() => updateTitle('testing'), item.id, 5000);

// After 5000ms uninterrupted seconds, item would finally get updated:

console.log(item);

// Only the title gets updated, because the title
// update was the last invocation under that signature :)
//
// {
//   id: "1234",
//   title: "testing",
//   date: "2024-04-23"
// }

The debouncer debounces any function call it wraps, globally, purely based on the signature.

The point is to extract messy debouncer management when you do want the debouncing condition to be a bit more arbitrary or independent of the place or time a function is called. This is especially useful in large applications where you only want to update data from one place at a time.

Best Practices

A recommendation when applying this package is to store all the signatures used across the app in one file, such that no accidental reuse of a signature occurs where it shouldn't.

It is advised to have a signatures.ts file somewhere containing an indexing object where all your signature definitions live, for example:

// signatures.ts

const signatureUseCaseOne: "describe the use case for readers/reviewers";
const signatureUseCaseTwo: "describe this use case too";

Note that if your use case intends to be based on some parameters, it could be a good idea to also store a function in this index:

// signatures.ts

const signatureUseCaseOne: "describe the use case for readers/reviewers";
const signatureUseCaseTwo: "describe this use case too";

const signatureUseCaseWithArgs: (args) => ({
  use_case: "description",
  params: { ...args }
});

This practice not only prevents the accidental reuse of signatures, but also enables a basic control click lookup of where signatures are being used to evaluate and debug your debouncers across the app.

This is of course a best practice and highly recommended, but not strictly necessary.

Built With

  • TypeScript - JavaScript that scales
  • Jest - Delightful JavaScript Testing

License

This project is licensed under the MIT License - see the LICENSE.md file for details