Fun with async hooks and stack traces
@naugtur, 2020
## Goal:
### Nerdy amusement with a chance of education
# ๐
Click for yourself:
https://naugtur.pl
https://github.com/naugtur/debugging-aid-demo
[![](CliffordStoll_2006-embed.jpg)](https://www.ted.com/talks/clifford_stoll_the_call_to_learn?utm_campaign=tedspread&utm_medium=referral&utm_source=tedcomshare)
It's going to be like this TED talk by Clifford Stoll if all goes well...
## I've got unusual hobbies
## ๐ง
### Trying seemingly impossible things in JavaScript is fun
# ๐ฃ
## Async hooks?
```js
const asyncHooks = require('async_hooks');
const hook = asyncHooks.createHook({
init,
before, after,
destroy
});
hook.enable();
```
### init
```js
Promise.resolve()
.then(๐() => {
return doingWork()
})
```
Runs within current call stack
### before
```js
Promise.resolve()
.then(() => ๐{
return doingWork()
})
```
### after
```js
Promise.resolve()
.then(() => {
return doingWork()
}๐)
```
### destroy
```js
Promise.resolve()
.then(() => {
return doingWork()
})
... ๐
```
There's also `promiseResolve` one now.
But instead of repeating documentation, I'll link to something you won't find in the docs:
https://github.com/nodejs/node/tree/master/deps/v8/test/mjsunit/async-hooks
## ๐งช ๐ฅ
## experiments!
# ๐ค
> How many promise objects do I produce?
```js
const functions = [inc,inc,inc]
const result = functions.reduce(
async (acc, f) => await f(await acc),
Promise.resolve(1)
)
const result = functions.reduce(
(acc, f) => acc.then(f),
Promise.resolve(1)
)
```
```js
const functions = [inc,inc,inc]
const result = functions.reduce(
async (acc, f) => await f(await acc),
Promise.resolve(1)
) // 14
const result = functions.reduce(
(acc, f) => acc.then(f),
Promise.resolve(1)
) // 5
```
```js
let count = 0;
const asyncHooks = require('async_hooks');
const asyncHook = asyncHooks.createHook({
init(asyncId, type, triggerAsyncId) {
if (type === 'PROMISE') count++;
},
});
asyncHook.enable();
process.on('beforeExit', () => {
console.log(`promises created: ${count}`);
});
```
This and other little experimental tools can be found in
[debugging-aid package](https://www.npmjs.com/package/debugging-aid)
# ๐ค
> Can I find the function that blocks the event loop?
```js
const timers = {}
before:(asyncId) => {
timers[asyncId] = Date.now()
}
after:(asyncId) => {
synchronousRunTime = Date.now() - timers[asyncId]
}
//you may be disappointed if you actually use Date.now()
```
### Ok, now which function was it?
```js
function whereAmI(){
const e = {}
Error.captureStackTrace(e)
return magicParsing(e.stack)
}
```
```
/home/naugtur/repo/app/app.js:14:4
```
# ๐
### Demo and deep dive
This functionality is available in a standalone package `blocked-at`
[see the file with hooks](https://github.com/naugtur/blocked-at/blob/master/index.js#L25)
# ๐ค
> Can I see which modules are using network?
```py
[aid] network, outgoing to: http://example.com/
stack: at Agent.createSocket (_http_agent.js:265:26)
at Agent.addRequest (_http_agent.js:224:10)
at new ClientRequest (_http_client.js:296:16)
at Object.request (http.js:46:10)
at Request.start (node_modules/request/request.js:751:32)
at Request.end (node_modules/request/request.js:1511:10)
```
# ๐ฟ
> Bonus - control network access for modules
[package-firewall](https://github.com/naugtur/package-firewall)
# ๐ค
> Can I visualize asynchronous flow of my app?
There's better tools for that, but hey - why not!
## DEMO
Thanks for participating
in my hobbies
@naugtur naugtur.pl