Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Something like async_hooks for the browser?

I'm trying to debug some (third party) code running in a browser context, and I'd like to track all downstream async function calls, pretty much exactly what async_hooks for nodejs provides. Essentially, something like https://github.com/mafintosh/why-is-node-running but in the browser. I'd settle for "something close", or even patching globals if I have to...

Anyone know of anything remotely close? This (https://stackoverflow.com/a/49245432/1256988) looked promising, but it doesn't appear to be working for sub-calls for me?!

I should point out that my practical use here is really just keeping a count of async function calls, so that at the end of my test, I know if I've "leaked" async calls.

I should additionally point out that this (https://github.com/AndreasMadsen/async-hook/issues/15) seems to indicate that I might be out of luck in the browser (though I'm ok with only approx. correct results).

like image 690
Carson Farmer Avatar asked Apr 06 '26 16:04

Carson Farmer


2 Answers

Zone.js does a pretty good job of this. It's what Angular uses internally to track state across asynchronous calls but it does exist as a separate API. It works by patching a whole load of browser APIs (such as setTimeout, event subscription, etc.)

Here's an example piece of HTML showing it in use. It runs up a couple of Zones (thread contexts, basically) and does asynchronous work in each zone. You can see the current zone magically being passed around during the flow of asynchronous work.

<html>
  <script src="https://unpkg.com/[email protected]/bundles/zone.umd.js"></script>
  <script>
    const rootZone = Zone.current;

    // Create two zones (thread contexts, basically)
    const zone1 = rootZone.fork({
      name: 'zone1',
      properties: { pizzaInfo: { topping: 'pepperoni' } },
    });
    const zone2 = rootZone.fork({
      name: 'zone2',
      properties: { pizzaInfo: { topping: 'pineapple' } },
    });

    // A function which will trigger some asynchronous work
    // Doesn't know anything about zones, but magically the
    // work will run in whatever zone launched it.
    function someWork() {
      setTimeout(someAsyncWork, 1000);
    }

    // Some asynchronous work
    function someAsyncWork() {
      const initialZone = Zone.current;
      const pizzaInfo = initialZone.get('pizzaInfo');

      console.log(`someAsyncWork: in zone ${initialZone.name}`);
      console.log(`someAsyncWork: best pizza topping for this zone is ${pizzaInfo.topping}`);

      // Have some async fun with promises and timers
      delay(500).then(yetMoreWork);
    }

    function yetMoreWork() {
      const zone = Zone.current;
      console.log(`yetMoreWork: in zone ${zone.name}`);
    }

    function delay(millis) {
      return new Promise((resolve) => setTimeout(resolve, millis));
    }

    zone1.run(someWork);
    zone2.run(someWork);
  </script>
</html>

(Here's a JSFiddle for that code: https://jsfiddle.net/rbkxd512/)

There's a quite a lot for Zone.js to patch in a browser, so it's broken into modules which you can select from depending on whether you care about multimedia APIs, network APIs, etc.

like image 195
Royston Shufflebotham Avatar answered Apr 08 '26 07:04

Royston Shufflebotham


Dexie.js has implemented a thing called PSD which allows for tracking async context -- even with native promises.

I pulled it out into its own package here: https://github.com/vlcn-io/model/tree/main/ts/packages/zone

You can see example of tracking an async function here: https://github.com/vlcn-io/model/blob/bb2907ca5034b345bfedd42af21944e10543b01d/ts/packages/value/src/transaction.ts#L106-L125

Note that the API isn't very user friendly yet and can be complicated to get the zone tracking set up correctly.

Real world tests and uses to get you started: https://github.com/vlcn-io/model/tree/main/ts/packages/value

like image 41
Matt Wonlaw Avatar answered Apr 08 '26 06:04

Matt Wonlaw



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!