0.1.0 • Published 5 months ago

als-unhooked v0.1.0

Weekly downloads
-
License
BSD-2-Clause
Repository
github
Last release
5 months ago

Asynchronous Local Storage ( UN-Hooked )

This is a fork of cls-hooked using AsyncLocalStorage instead of async_hooks.

Since the Node team now discourages the use of async_hooks, this package aims to provide a drop-in replacement for cls-hooked using async_hooks successor, the AsyncLocalStorage API (which is officially stable, by the way).

Use with Sequelize (v6)

A major motivator in creating this package was for use with sequelize v6, which uses cls-hooked for automatic transaction passing. This package is not officially supported by sequelize at this time, but the modern API has been designed to be compatible with sequelize's implementation of cls-hooked.

// app.js

import { Sequelize } from 'sequelize';
import { ALS } from 'als-unhooked';

Sequelize.useCLS(new ALS());

Thanks to @jeff-lewis for cls-hooked, and to the many others who have contributed to async context tracking in node over the years.


Continuation-local storage works like thread-local storage in threaded programming, but is based on chains of Node-style callbacks instead of threads. The standard Node convention of functions calling functions is very similar to something called "continuation-passing style" in functional programming, and the name comes from the way this module allows you to set and get values that are scoped to the lifetime of these chains of function calls.

Suppose you're writing a module that fetches a user and adds it to a session before calling a function passed in by a user to continue execution:

// setup.js

import als from 'als-unhooked';
import db from './lib/db.js';

function start(options, next) {
  db.fetchUserById(options.id, function (error, user) {
    if (error) return next(error);

    als.set('user', user);

    next();
  });
}

Later on in the process of turning that user's data into an HTML page, you call another function (maybe defined in another module entirely) that wants to fetch the value you set earlier:

// send_response.js

import als from 'als-unhooked';
import render from './lib/render.js';

function finish(response) {
  const user = als.get('user');
  render({user: user}).pipe(response);
}

When you set values in AsyncLocalStorage, those values are accessible until all functions called from the original function – synchronously or asynchronously – have finished executing. This includes callbacks passed to process.nextTick and the timer functions (setImmediate, setTimeout, and setInterval), as well as callbacks passed to asynchronous functions that call native functions (such as those exported from the fs, dns, zlib and crypto modules).

A simple rule of thumb is anywhere where you might have set a property on the request or response objects in an HTTP handler, you can (and should) now use AsyncLocalStorage. This API is designed to allow you extend the scope of a variable across a sequence of function calls, but with values specific to each sequence of calls.

While cls-hooked used namespaces, als-unhooked relies on instances of the ALS class. Most will not have need for multiple instances, though, so a default instance is bound to the ALS class itself as static methods for convenience.

One difference in the main implementation is that, unlike cls-hooked, nested calls to als.run() do not automatically inherit their parent context. You can easily remedy this by passing als.getStore() as the second argument to als.run().

ToDo

  • detailed documentation

copyright & license

See LICENSE for the details of the BSD 2-clause "simplified" license used by als-unhooked.

0.1.0

5 months ago

0.0.2

5 months ago

0.0.1

5 months ago