'AsyncLocalStorage losing context in response event handler

Here is a simple server that shows the issue:

const express = require("express");
const { AsyncLocalStorage } = require('async_hooks');

const als = new AsyncLocalStorage();

port = 3000;

class store {}

async function sleep(ms) {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
}

async function handle(res) {
  console.log(`In handle, context is ${als.getStore() === undefined ? 'un' : ''}defined`);
  res.on('close', () => {
    if (!res.writableEnded) {
      console.log(`In close, context is ${als.getStore() === undefined ? 'un' : ''}defined`);
    }
  });
  await sleep(30000);
  return 'Hello World';
}

createServer = () => {
  const app = express();
  app.get("/", async (req, res) => {
    const ret = await als.run(new store(), async () => handle(res));
    res.send(ret);
  });
  return app;
}

const app = createServer();
const server = app.listen(port, () => {
  console.log(`I'm listening at http://localhost:${port}`);
});

With the server running, if you send a request - e.g. curl http://localhost:3000/ and then interrupt the curl command with ^C, the server shows:

[~/git/als-test] node server.js
I'm listening at http://localhost:3000
In handle, context is defined
In close, context is undefined

I'm guessing I need to use AsyncResource, but from the docs I'm not entirely sure how that would plug in to my example.



Solution 1:[1]

It seems the solution was very simple and did indeed involve AsyncResource. I just had to change:

  res.on('close', () => {
    if (!res.writableEnded) {
      console.log(`In close, context is ${als.getStore() === undefined ? 'un' : ''}defined`);
    }
  });

to:

  res.on('close', AsyncResource.bind(() => {
    if (!res.writableEnded) {
      console.log(`In close, context is ${als.getStore() === undefined ? 'un' : ''}defined`);
    }
  }));

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Horrendo