The AsyncListener API allows you to have callbacks executed over the life cycle of an asynchronous callback. This allows you to solve a number of problems like storing transaction ids, having long stack traces, and profiling asynchronous functions. In this post I’m going to dive into the AsyncListener API, answer a few common questions, and introduce you to how it works.
In a previous post I talked about preserving data across asynchronous callbacks. If you haven’t read it, it talks about how to store a transaction id for every request and then automatically add the transaction id to every log message. It’s great to be able to use continuation-local-storage, but if you’re like me the next question you have is: how does it work?
The AsyncListener API is part of Node.js core and was released in version 0.11.9. It is still in the process of becoming stable, but it is useable today because it was backported to earlier versions of Node.js with the module async-listener, we use it in Node.js 0.10 and I have tested it in Node.js 0.8 .
This post will take a QA format and by the end of it you should be comfortable with using packages that use the AsyncListener API.
The AsyncListener API allows you to have callbacks called during the life-cycle of an asynchronous function.
More specifically your callbacks are called when an asynchronous function is queued (via process.nextTick), before it is executed, after it is executed, or if it errors.
Here you can see the points at which our code is executed as an asynchronous function moves through the event loop. If you’re not familiar with the event loop, take a look at this stackoverflow answer and this post about the event loop.
Node.js is asynchronous and uses an event loop to manage the asynchronicity, which causes a couple side effects. The order in which callbacks are called is not guaranteed. When a callback is called, the stack where it was created is not available. This causes problems like having stack traces only go back to the nearest tick, not being able to reason about state after a callback is called, and not knowing when (if ever) a callback might get called.
AsyncListener allows you to write software that can keep hold of information across callbacks. Before you would have to pass arguments everywhere, use closures, or create a hack upon Node.js domains. Now you can simply have Node.js manage the information for you, whether that information is an extended stack trace, profiling, or just which web request lead to a code path. The solution is the same.
Currently the API hasn’t quite stabilized, but because of a backport it’s still possible (and simple) to use it today. Actually, if you’re using New Relic for Node.js the AsyncListener API is already in use by your application.
The AsyncListener API was released in Node 0.11.9, and was backported to earlier versions of Node.
The interface for how to create an AsyncListener object changed between versions 0.11.11 and 0.11.12 (more on that below). Finally, it was removed in the tentative Node 0.12 since it hasn’t quite stabilized yet.
If you’re on Node < 0.11.9 go for it!
The polyfill works great, and we use it in production with Node 0.10. The polyfill is called ‘async-listener’ and is available on npm.
If you’re on Node >= 0.11.9 you can still use it fairly easily, but you’ll have to look into the specific interface for creating an AsyncListener.
For Node 0.11.9 to 0.11.11, see the documentation in process for your specific version.
For Node 0.11.12 and up, look at the documentation in tracing.
For everyone, expect that the API will shift slightly when it’s available with Node 0.12.
With the AsyncListener API you can create a profiler, thread local storage, or finally get long stack traces. If you’re feeling creative you could use it to extract “progress” information from your application. You could also use it check whether an asynchronous function ever leads to calling another asynchronous function. Previous scenarios that were very very difficult just became a lot easier.
A full-blown example is enough to be it’s own post. If you’re curious about using AsyncListener I’d recommend first checking out the documentation for the API. Then learn by example and take a look at the source of async-profile or stackup. The former implements a profiler for asynchronous functions and the latter implements long stack traces. While reading the source, just remember that they are only tapping into the life-cycle of an asynchronous callback.
I hoped this helped you feel more comfortable with the AsyncListener API. As always feel free to post questions about it in the comments. If you do use the API, or have any neat ideas of uses I’d love to hear about them as well.
Get the fastest, easiest way to understand your data today.Sign up for free