# Bring Thread-Local-Storage / Async\_Hooks into the language

**URL:** <https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [December 12, 2022, 5:50pm UTC](https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552 "2022-12-12T17:50:54Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![bryanerayner](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bryanerayner/32/1589_2.png) [@bryanerayner](https://es.discourse.group/u/bryanerayner)\
**Post date:** [December 12, 2022, 5:50pm UTC](https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552/1 "2022-12-12T17:50:55Z")

</div>

My company has adopted the use of thread-local-storage (via Zone.js) to allow the same code to run in two different contexts; The first, and default context is one in which the code actively navigates the user to another location in the browser. The second context is where we capture the final URL instead, so that it can be inserted into the href of an anchor tag.

At bare minimum, I need to know "what triggered the execution of this callback?". Having access to "Thread local storage" (Zone.current in my case combined with a WeakMap) has allowed the code to be less error prone, as we're not forced to pass a context object around with every function invocation in the stack. It has allowed our application to comply with WCAG guidelines more completely because what was previously a click handler function, can now be expressed as a full HREF. It always works, because it's the exact same codepath, save for whether or not navigation occurs or it's just captured.

Node.JS has the `async_hooks` feature, and for now, I have Zone.js. However, it would be a lot easier knowing the rug isn't going to be pulled out from underneath me; Zone.js is being deprecated because of its inability to handle async / await, and without a replacement I'll be unable to use this very powerful technique.

While we can still operate without native support for this feature, the confidence we have that link generation will be correct is something I don't want to lose. Alternatives would require a rewrite with a context object passed through at every step, and I feel like Javascript can be better than that.

---

<div class="post-metadata">

**Author:** ![bryanerayner](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bryanerayner/32/1589_2.png) [@bryanerayner](https://es.discourse.group/u/bryanerayner)\
**Post date:** [December 12, 2022, 5:53pm UTC](https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552/2 "2022-12-12T17:53:27Z")

</div>

For reference, here is the critical code I will be unable to use if I lose access to Zone.js and don't have an alternative.

```javascript
@Injectable({
  providedIn: 'root',
})
export class FlowNavRecorderService {
  get useRecorder(): boolean {
    return !!this._useRecorder.get(Zone.current);
  }

  private _capturedRoute = new WeakMap<
    Zone,
    BehaviorSubject<IRoute | { href: string } | null>
  >();
  private _useRecorder = new WeakMap<Zone, boolean>();

  getPlannedRoute() {
    return this._capturedRoute.get(Zone.current);
  }
  resetPlannedRoute() {
    this._capturedRoute.get(Zone.current)?.next(null);
  }

  constructor() {}

  recordPlannedAction(plannedRoute: IRoute | { href: string }) {
    this._capturedRoute.get(Zone.current).next(plannedRoute);
  }

  private _it = 0;

  capturePlannedRoute(
    doNavigation: () => void
  ): Promise<IRoute | { href: string }> {
    const zone = Zone.current.fork({
      name: 'flow-nav-recorder-capture--' + this._it++,
    });
    const { _capturedRoute, _useRecorder } = this;
    return new Promise<IRoute | { href: string }>((resolve, reject) => {
      const route$ = new BehaviorSubject<IRoute | { href: string } | null>(
        null
      );
      _capturedRoute.set(zone, route$);
      _useRecorder.set(zone, true);
      zone.run(() => {
        try {
          doNavigation();
        } catch {}
        route$.subscribe(
          (r) => {
            if (!r) {
              return;
            }
            resolve(r);
            route$.complete();
            _useRecorder.delete(zone);
            _capturedRoute.delete(zone);
          },
          (err) => {
            reject(err);
          }
        );
      });
    });
  }

}

```

If I had access to async\_hooks, I'm confident I could do a rewrite without too many downstream effects. What can we do here? Can it be rolled into the `async do` proposal?

---

<div class="post-metadata">

**Author:** ![aclaymore](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/aclaymore/32/501_2.png) [@aclaymore](https://es.discourse.group/u/aclaymore)\
**Post date:** [December 12, 2022, 9:11pm UTC](https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552/3 "2022-12-12T21:11:43Z")

</div>

[GitHub - legendecas/proposal-async-context](https://github.com/legendecas/proposal-async-context) was on the agenda for TC39 last month, but unfortunately there wasn't enough time so it's been deferred to a future meeting.

Slides: [AsyncContext - Google Slides](https://docs.google.com/presentation/d/1yw4d0ca6v2Z2Vmrnac9E9XJFlC872LDQ4GFR17QdRzk/edit?usp=sharing)

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [December 13, 2022, 1:07am UTC](https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552/4 "2022-12-13T01:07:34Z")

</div>

> [@bryanerayner](#):
>
> At bare minimum, I need to know "what triggered the execution of this callback?". Having access to "Thread local storage" (Zone.current in my case combined with a WeakMap) has allowed the code to be less error prone, as we're not forced to pass a context object around with every function invocation in the stack.

So the needle has to be threaded very carefully here. In order to avoid full on dynamic scoping, unconditionally exposing information allowing a function to sense the caller is not going to be acceptable. In that sense, a global like `Zone.current` is not a solution.

The `AsyncContext` proposal takes pain to avoid the dynamic scoping issues by requiring the function to hold a reference to the `AsyncContext` instance. It requires a predecessor (synchronous or asynchronous) to initialize the context's content, and for the program to arrange the same context instance being shared between the predecessor and the function that requires the context's data. That allows a set of related functions to share "stack" state independently of the interleaving functions on the stack.

Of course at that point nothing prevents the functions using such a context from having a behavior observably different outside of the functions sharing a reference to the context, but good practices should avoid that as it would recreate the problems caused by dynamic scoping.

---

<div class="post-metadata">

**Author:** ![bryanerayner](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bryanerayner/32/1589_2.png) [@bryanerayner](https://es.discourse.group/u/bryanerayner)\
**Post date:** [December 13, 2022, 5:29am UTC](https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552/7 "2022-12-13T05:29:19Z")

</div>

That gives me hope.

Is the prospect good at getting it in browsers within the next 1, 2 years?

---

<div class="post-metadata">

**Author:** ![mhofman](https://avatars.discourse-cdn.com/v4/letter/m/f14d63/32.png) [@mhofman](https://es.discourse.group/u/mhofman)\
**Post date:** [December 13, 2022, 10:11am UTC](https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552/8 "2022-12-13T10:11:12Z")

</div>

I can't guarantee how timing will work out for this proposal, or if it will make it at all, it really depends.

One thing though is that this proposal has a new co-champion, @jridgewell, who is very interested in getting it through, so I'd be hopeful something will come out of it that should solve your use case, even if it doesn't look exactly like the current proposal.

---

<div class="post-metadata">

**Author:** ![bryanerayner](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bryanerayner/32/1589_2.png) [@bryanerayner](https://es.discourse.group/u/bryanerayner)\
**Post date:** [December 14, 2022, 4:36am UTC](https://es.discourse.group/t/bring-thread-local-storage-async-hooks-into-the-language/1552/9 "2022-12-14T04:36:46Z")

</div>

Yes, zones isn't really the necessary part, async context would be enough. How can we help @jridgewell make a case for it?
