# @webfill/async-context

> 🗺️ An experimental AsyncContext polyfill

Latest version **1.0.0** (published 2023-07-22) · MIT license · 0 weekly downloads

## Install

```sh
npm install @webfill/async-context
pnpm add @webfill/async-context
yarn add @webfill/async-context
bun add @webfill/async-context
```

## Health

**Score 20/100 (F)** — status: abandoned.

Positive: esm support; no vulnerabilities.

Warnings: low downloads; no types.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.0 |
| Published | 2023-07-22 |
| First published | 2023-07-22 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | none |
| Module format | ESM |
| Dependencies | 1 |
| Unpacked size | 11.2 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Maintainers | jcbhmr |
| Keywords | nodejs, polyfill, experimental, async, proposal, zonejs, async-context |

## Links

- npm: https://www.npmjs.com/package/@webfill/async-context
- Repository: https://github.com/webfill/async-context
- Homepage: https://github.com/webfill/async-context#readme
- Issues: https://github.com/webfill/async-context/issues
- npm.io page: https://npm.io/package/@webfill/async-context

## Dependencies (1)

- [zone.js](https://npm.io/package/zone.js.md) ^0.13.1

## Alternatives

- [@commercetools/sync-actions](https://npm.io/package/@commercetools/sync-actions.md) — 25.1K weekly downloads
- [cwait](https://npm.io/package/cwait.md) — 21.4K weekly downloads
- [@ledgerhq/hw-app-cosmos](https://npm.io/package/@ledgerhq/hw-app-cosmos.md) — 4.2K weekly downloads
- [@financial-times/o-loading](https://npm.io/package/@financial-times/o-loading.md) — 2.8K weekly downloads
- [fa](https://npm.io/package/fa.md) — 185 weekly downloads

## Recent versions

- 1.0.0 (latest) — 2023-07-22

## README

# `AsyncContext` polyfill for Node.js and the browser

🗺️ An [experimental `AsyncContext`] polyfill

<div align="center">

![](https://i.imgur.com/vLhU6eA.png)

</div>

⚠️ Experimental API \
🎣 Uses Node.js' [`AsyncLocalStorage`] if possible \
🧅 Works with Bun via [Zone.js] \
🦕 Works with Deno via [their Node.js compat layer] \
🌐 Works in the browser via [Zone.js]!

## Installation

![npm](https://img.shields.io/static/v1?style=for-the-badge&message=npm&color=CB3837&logo=npm&logoColor=FFFFFF&label=)
![Yarn](https://img.shields.io/static/v1?style=for-the-badge&message=Yarn&color=2C8EBB&logo=Yarn&logoColor=FFFFFF&label=)
![pnpm](https://img.shields.io/static/v1?style=for-the-badge&message=pnpm&color=222222&logo=pnpm&logoColor=F69220&label=)
![jsDelivr](https://img.shields.io/static/v1?style=for-the-badge&message=jsDelivr&color=E84D3D&logo=jsDelivr&logoColor=FFFFFF&label=)

This package can be installed locally using npm, [Yarn], [pnpm], or your other
favorite package manager of choice:

```sh
npm install @webfill/async-context
```

If you're using Deno, you can install this package using the new [`npm:`
specifiers], or directly from a Deno-compatible npm CDN like [esm.sh]:

```js
import {} from "npm:@webfill/async-context";
import {} from "https://esm.sh/@webfill/async-context";
```

If you want to use this package in the browser without needing a build tool to
bundle your npm dependencies, you can use an npm CDN like [esm.sh] or [jsDelivr]
to import it directly from a URL:

```js
import {} from "https://esm.sh/@webfill/async-context";
import {} from "https://esm.run/@webfill/async-context";
```

## Usage

![Node.js](https://img.shields.io/static/v1?style=for-the-badge&message=Node.js&color=339933&logo=Node.js&logoColor=FFFFFF&label=)
![Deno](https://img.shields.io/static/v1?style=for-the-badge&message=Deno&color=000000&logo=Deno&logoColor=FFFFFF&label=)
![Browser](https://img.shields.io/static/v1?style=for-the-badge&message=Browser&color=4285F4&logo=Google+Chrome&logoColor=FFFFFF&label=)
![Bun](https://img.shields.io/static/v1?style=for-the-badge&message=Bun&color=000000&logo=Bun&logoColor=FFFFFF&label=)

This package exports the `AsyncContext` namespace. To get started, you can
create a new `AsyncContext.Variable` and use it in various places. When you use
the `.run(value, f)` method, it will cascade that value throughout the entire
(possibly asynchronous) execution of any subsequent functions. Here's a quick
demo:

```js
import AsyncContext from "@webfill/async-context";

const message = new AsyncContext.Variable({ defaultValue: "Hello" });

message.run("Hi", async () => {
  await fetch("https://jsonplaceholder.typicode.com/todos/1");
  console.log(message.get());
  //=> "Hi"
});

message.run("Hey", () => {
  setTimeout(() => {
    console.log(message.get());
    //=> "Hey"
  }, 10);
});

console.log(message.get());
//=> "Hello"
```

For a more practical example, you could use an `AsyncContext.Variable` to track
a `Request`'s ID across many different asynchronous functions **without
resorting to "argument drilling"**:

```js
const id = new AsyncContext.Variable();
let i = 0;
globalThis.addEventListener("fetch", (event) => {
  id.run(++i, () => {
    event.respondWith(handleRequest(event.request));
  });
});

function logError(message) {
  // Note that this is two calls deep in an async chain! Yet we still get the
  // correct ID that was set via 'id.run()'
  console.error(id.get(), message);
  //=> '1' 'Not found'
  //=> '2' 'Not found'
}

async function handleRequest(request) {
  if (request.url === "/") {
    await doThing();
    return new Response(`Hello, ${id.get()} 👋`);
    //=> 'Hello, 1 👋'
    //=> 'Hello, 2 👋'
  } else {
    await doThing();
    logError("Not found");
    return new Response(`${id.get()} not found.`, { status: 404 });
    //=> '1 not found.'
    //=> '2 not found.'
  }
}
```

Here's the example from the proposal for reference.

> ```js
> const asyncVar = new AsyncContext.Variable();
>
> // Sets the current value to 'top', and executes the `main` function.
> asyncVar.run("top", main);
>
> function main() {
>   // AsyncContext.Variable is maintained through other platform queueing.
>   setTimeout(() => {
>     console.log(asyncVar.get()); // => 'top'
>
>     asyncVar.run("A", () => {
>       console.log(asyncVar.get()); // => 'A'
>
>       setTimeout(() => {
>         console.log(asyncVar.get()); // => 'A'
>       }, randomTimeout());
>     });
>   }, randomTimeout());
>
>   // AsyncContext.Variable runs can be nested.
>   asyncVar.run("B", () => {
>     console.log(asyncVar.get()); // => 'B'
>
>     setTimeout(() => {
>       console.log(asyncVar.get()); // => 'B'
>     }, randomTimeout());
>   });
>
>   // AsyncContext.Variable was restored after the previous run.
>   console.log(asyncVar.get()); // => 'top'
>
>   // Captures the state of all AsyncContext.Variable's at this moment.
>   const snapshotDuringTop = new AsyncContext.Snapshot();
>
>   asyncVar.run("C", () => {
>     console.log(asyncVar.get()); // => 'C'
>
>     // The snapshotDuringTop will restore all AsyncContext.Variable to their snapshot
>     // state and invoke the wrapped function. We pass a function which it will
>     // invoke.
>     snapshotDuringTop.run(() => {
>       // Despite being lexically nested inside 'C', the snapshot restored us to
>       // to the 'top' state.
>       console.log(asyncVar.get()); // => 'top'
>     });
>   });
> }
>
> function randomTimeout() {
>   return Math.random() * 1000;
> }
> ```

&mdash; [Proposed Solution | Async Context for JavaScript]

<!-- prettier-ignore-start -->
[experimental `AsyncContext`]: https://github.com/tc39/proposal-async-context#readme
[`AsyncLocalStorage`]: https://nodejs.org/api/async_context.html#async_context_class_asynclocalstorage
[their Node.js compat layer]: https://github.com/denoland/deno/tree/main/ext/node/polyfills#readme
[Zone.js]: https://www.npmjs.com/package/zone.js
[Proposed Solution | Async Context for JavaScript]: https://github.com/tc39/proposal-async-context#proposed-solution
[`npm:` specifiers]: https://deno.land/manual/node/npm_specifiers
[esm.sh]: https://esm.sh/
[jsDelivr]: https://www.jsdelivr.com/esm
[Yarn]: https://yarnpkg.com/
[pnpm]: https://pnpm.io/
<!-- prettier-ignore-end -->

---
_Source: https://npm.io/package/@webfill/async-context · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
