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

@danscode/futures

v2.1.2

Published

Implementation of a Future object for vanilla javascript

Readme

About

This is an implementation of a Future object for vanilla javascript.
A Future extends Promise and partially unwraps itself once it is settled.
It improves the reusability of Promise objects, without the need to reassign variables or re-await it.

Overview

The initialization is very similar to a Promise and has some QoL improvements.
A Future extends Promise for 2 reasons:

  1. to inherit all the Promise methods (e.g. .then() or .catch()), even from future ES specs.
  2. because they are very similar concepts.

[!IMPORTANT]
Requires ES2022 and ESM imports.

Available to install via npm i @danscode/futures or deno install npm:@danscode/futures.
You can grab a test file here.

Initialization and Syntax

The accepted function is run immediately, and a Future is returned.
A Future accepts both regular and async executor functions.

const futuro = new Future((resolve, reject, signal, ...) => {
  /*your code*/
});
const futuroAsincrono = new Future(async (signal, ...) => {
  /*your code*/
});

Some handles will be passed to the executor, it depends on the kind of function used as executor:

  • These args are always sent, they can be called anything.
  • You can pass any extra args to the executor like so new Future(executor, 0, a, b, c, ...).
  • Regular executor is wrapped in a Promise and you can manually reject() or resolve() it.
  • Async executor implicitly returns a Promise so you cannot manually settle the async Promise.

A Future accepts an options object:

| Property | Description | | :-------: | :-------------------------------------------------------------- | | .signal | accepts an AbortSignal object (is always passed to executors) |

Structure

A Future has exposed, readonly properties (the object itself remains extensible):

| Property | Description | | :----------: | :---------------------------------------------------------------------- | | .value | is the settled value of the underlying Promise | | abort() | is a reference to the method of an AbortController | | .state | mimicks internal slot [[PromiseState]] | | .isPending | returns false for resolved or rejected Future |

Details:

  • initially null
  • if resolved it is the resolved value
  • if rejected it is the rejected value
  • if thrown it is the error object
  • if no signal was passed to the Future constructor, it is present but undefined.
    • this prevents accidental abort of other dependants when you only meant to abort the one Future;
    • also AbortSignal has no reference to AbortController.abort(), so I can't grab it.
  • once the Future is settled, it is null (for memory cleanup).
  • see abort example

Usage

The main use case is to achieve some performance gain by avoiding frequent use of await.
In other words it's like a let data = new Promise() with more secure automatic unwrapping.
Promise always has to be unwrapped (even if already resolved) with await or .then() otherwise the value is inaccessible.
But await schedules a microtask just like .then().
This means every await surrenders this iteration of the event loop, because a microtask is only executed between iterations of the event loop.
Also an async function will be frozen untill it resolves its first await and then the next, and the next...

This is an example of a busy program with the hot path in red:
The following diagram implies that promises might also call/contain long tasks

%% if you see this text try https://mermaid.live/

flowchart LR
    NAME["Event Loop (simplified)"]
    stack@{ shape: lin-cyl, label: "Check Call Stack" }
    stack--- checkT{"Has a task"}
    checkT--- |Yes| busyT@{shape: processes, label: "Execute code..."}
    checkT--- |No| mTstack@{ shape: lin-cyl, label: "Check Microtask Queue" }
    busyT-- "Build the stack up and down" ---stack
    mTstack--- checkmT{"Has a microtask"}-.-x |Yes| busyT

  %% Highlight the edges in the hot path
    linkStyle 0,1,3 stroke:#ff1f1f,stroke-width:5px
  %% cold path
    linkStyle 2,4 stroke:#3464ff,stroke-width:2px

As you can see, every time you have to await, your code takes a passing loop.
It will only execute after the entire call stack has been emptied.

There is one trick to await the value at the very last moment, letting the function start other Promises and complete some synchronous setup.

async function longTask() {
  const file1 = fileAsync('bunny.png');
  const file2 = fileAsync('carrot.png');
  let number = numberAsync(1);
  // long setup
  for (let i = 0; i < 100000; i++) {
    const x = i * 3;
  }

  // actual use of data
  number = await number;
  return (await file1) + (await file2) + number;
}

I find several problems with this approach, specifically in more complicated production grade code:

  1. We still can only get the value out by awaiting again.
    • what if we need to use const file1 more than once?
  2. We could reassign let number to the once awaited value.
    • very easy to reassign it somewhere else by accident.
  3. We could start all Promises with Promise.all() which still has to be unwrapped with an await.
    • and possibly destructured into more variables;
    • what if there are more Promises dependent on earlier Promises?
    • what if Promises are best awaited at different points in the function?

Future removes the need for tricks, you only need to await it once to receive the value.
Any later access is direct and synchronous via <future>.value.
A Future also integrates with AbortSignal in two ways:

  1. You can pass an AbortController.signal object in the Future constructor options.
  2. Or an AbortController will be created.
    • this makes sure the AbortSignal is always passed into executors;
    • and exposes the .abort() method on the Future itself.

Here is an example of how to use it:

const cancelledFuture = new Future((res, rej, signal) => {
  signal.addEventListener(
    'abort',
    () => {
      rej(signal.reason);
    },
    { once: true }
  );
  // never resolves
  setTimeout(() => {
    res(200);
  }, 1500);
});

try {
  cancelledFuture.abort(`I don't want this`);
  console.log(await cancelledFuture);
} catch (e) {
  console.log(e);
}

Contributors

Dan: Code author

Changelog

  • improved natural async function detection
  • changed <future>.v to <future>.value