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

@sabl/async-test

v0.5.0

Published

Basic utilities for testing asynchronous programs

Readme

codecov

version: 0.5.0 | tag: v0.5.0 | commit: 60f7c80b8

@sabl/async-test

async-test contains several simple utilities for testing concurrent or async programs in JavaScript and TypeScript. It builds on @sabl/async, which contains utilities that are useful in both test and production scenarios.

API

later

later<T>(fn: () => T, ms?: number): Promise<T>
later<T>(fn: () => Promise<T>, ms?: number): Promise<T>
later<T>(value: T, ms?: number): Promise<T>
later<T>(promise: Promise<T>, ms?: number): Promise<T>

later resolves a value or function after a delay. It wraps JavaScript's setTimeout as a promise. It is especially helpful in testing async programs to create artificial delays that work with await. The input can be any of the following:

Note that if the ms timeout is less than or equal to zero, the call will be rejected immediately even if an input promise was already resolved.

Synchronous function

With a synchronous function, later works identically to setTimeout except that it returns a promise.

// Before
setTimeout(() => console.log('hello'), 10);

// After 
later(() => console.log('hello'), 10); 

The promise resolves to whatever value is returned by the callback.

const promise = later(() => {
  console.log('hello');
  return 'world'
}, 10); 
const result = await promise;
console.log(result); // world

Async function

If the value provided to later itself returns a promise, then that promise will be awaited before later resolves. Note that the callback will not be started until the timeout expires, so the total time before the function resolves may be longer than ms. If you simply wish to set a timeout for how long an async function may take, use limit.

const promise = later(() => Promise.resolve('hello'), 10);
const result = await promise;
console.log(result); // hello

Existing promise

An existing promise will be await ed after the timeout to resolve the final value.

const innerPromise = Promise.resolve('hello');
const promise = later(innerPromise, 10);
const result = await promise;
console.log(result); // hello

Literal value

Any value that is not a function or promise will be used as is to resolve the promise after the timeout:

const promise = later('hello', 10);
const result = await promise;
console.log(result); // hello

Timeline

export class Timeline {
  constructor(tickMs?: number);

  get tick(): number;
  get running(): boolean;
  get drained(): boolean;

  setTimeout(cb: () => unknown, ticks: number): number;
  clearTimeout(id: number): boolean;
  wait(ticks: number): Promise<void>;
  
  start(): void;
  reset(): Promise<void>;
  next(): Promise<void>;
  drain(): Promise<void>;
}

Timeline schedules callbacks to be executed in a deterministic order one frame at a time. It designed to set up tests of async programs where the exact order of async events matters. Frame numbers add up intuitively for understandable ordering.

Timeline's setTimeout and clearTimeout methods are replacements for using the otherwise builtin setTimeout and clearTimeout, which do not actually guarantee that a callback will be executed after the exact number of ms specified. `

constructor

new Timeline(tickMs?: number) 

The constructor accepts a single options parameter tickMs, which determines the number of platform ms to wait before starting the next tick. If null, there is no pause between ticks but the timeline will idle when drained.

If 0 or positive, the platform setTimeout(..., tickMs) will be awaited between ticks, and ticking will continue until the timeline is reset even if there are no callbacks scheduled.

tick

The current tick number.

running

Whether the timeline is running.

drained

true if there are no scheduled callbacks

setTimeout(fn, ticks = 0)

Schedule a callback to be executed ticks ticks in the future. Can be called from within a callback. Returns an id which can be used to clear the callback.

If ticks is exactly 0:

  • If timeline has not yet started, callback will be invoked before the first (frame 1) tick
  • If timeline is running, callback will be invoked at the end of the current tick
  • Same-frame scheduling is respected recursively, including for async callbacks

clearTimeout(id)

Clear a previously scheduled callback using its id value as returned from setTimeout.

wait(ticks)

Returns a promise which will be resolved after ticks ticks. Useful for succinctly awaiting in tests to let a certain number of frames complete, regardless of how long that takes in real time.

start()

Start the timeline.

  • If tickMs provided to constructor is >= 0

    Timeline will continue ticking indefinitely until reset() is called, and will wait tickMs milliseconds between ticks using the platform setTimeout.

  • If tickMs is empty (default)

    Timeline will cycle without stopping through all scheduled frames. When all callbacks have been cleared or called, the timeline will pause until setTimeout is called again.

drain()

Returns a promise which will resolve when all scheduled callbacks have been cleared or executed.

reset()

Cancels and clears all pending callbacks, stops ticking, and resets tick number to 0.

next()

Advance a single tick on a timeline that was never started. Can be used to manually tick forward one frame at a time. Returns a promise which resolves when all callbacks from the frame have been executed, and any promises returned from them have resolved or rejected.

Example

A contrived example that demonstrates deterministic ordering and additive frame numbers:

const tl = new Timeline();
const log: string[] = [];
const logTick = (msg: string) => log.push(`tick ${tl.tick} : ` + msg);

tl.setTimeout(() => logTick('E @ 3'), 3); 

tl.setTimeout(() => { 
  tl.setTimeout(() => logTick('G @ 3 + 3 = 6'), 3);
}, 3);

tl.setTimeout(async () => {
  logTick('A @ 1');  
  
  await new Promise(resolve => setTimeout(resolve, 500));

  tl.setTimeout(() => {
    logTick('B @ 1 + 1 = 2'); 

    tl.setTimeout(() => logTick('F @ 1 + 1 + 3 = 5'), 3);
    tl.setTimeout(() => logTick('C @ 1 + 1 + 0 = still 2'), 0);
    tl.setTimeout(() => {
      tl.setTimeout(() => {
        tl.setTimeout(() => {
          logTick('D @ 1 + 1 + 0 + 0 + 0 = *still* 2')
        }, 0);
      }, 0);
    }, 0);
  }, 1);
}, 1);

tl.setTimeout(() => logTick('pre-tick @ 0')); // Can omit 0 timeout

tl.start();

await tl.drain();

console.log(log.join('\n'));

// tick 0: pre-tick @ 0
// tick 1: A @ 1 
// tick 2: B @ 1 + 1 = 2
// tick 2: C @ 1 + 1 + 0 = still 2
// tick 2: D @ 1 + 1 + 0 + 0 + 0 = *still* 2
// tick 3: E @ 3
// tick 5: F @ 1 + 1 + 3 = 5
// tick 6: G @ 3 + 3 = 6