Breaking the async boundary
James Allardice
The event loop
- Used by JS engines to schedule tasks
-
Most callbacks (e.g.
setTimeout
) are tasks - Tasks are scheduled to run sequentially
- Different tasks have different stack frames
The problem
startTimer();
a();
setTimeout(b, 0);
c();
stopTimer();
How long does it take the following program, including `b`, to execute?
startTimer();
a();
setTimeout(b, 0); // asynchronous
c();
stopTimer();
startTimer(); // 1
a();
setTimeout(b, 0); // asynchronous
c();
stopTimer();
startTimer(); // 1
a(); // 2
setTimeout(b, 0); // asynchronous
c();
stopTimer();
startTimer(); // 1
a(); // 2
setTimeout(b, 0); // asynchronous
c(); // 3
stopTimer();
startTimer(); // 1
a(); // 2
setTimeout(b, 0); // asynchronous
c(); // 3
stopTimer(); // 4
startTimer(); // 1
a(); // 2
setTimeout(b, 0); // asynchronous // 5
c(); // 3
stopTimer(); // 4
The problem
How can we associate data with the HTTP request in the following program?
// Assume `app` is an Express instance
app.use((req, res, next) => {
// Generate a unique identifier for this request.
const requestId = generateId();
next();
});
// Assume `app` is an Express instance
app.use((req, res, next) => {
// Generate a unique identifier for this request.
const requestId = generateId();
// Attach the identifier to the request object.
req.id = requestId;
next();
});
The problem
How can we capture a full stack trace when the following program dies?
Solutions
- Domains
- Async Listener
- Continuation Local Storage (CLS)
- AsyncWrap
- Zones
Domains
- Introduced in 0.8 but now deprecated (not yet removed)
- "Handle multiple different IO operations as a single group"
Async Listener
- Pull request to Node core never merged
- Polyfill exists
- Provides a mechanism to run functions just before and after a new task begins and ends
Continuation Local Storage
- Built on Async Listener
- Associate values with a namespace
- Namespaces are groups of functions, synchronous or otherwise
- Retrieve value from a namespace in any function in that group
AsyncWrap
- Undocumented API
- Available via
process.binding('async_wrap')
Zones
- Stage 0 ECMAScript proposal
- Inspired by Dart
- Numerous existing implementations (Angular zone.js, StrongLoop zone, can-zone)
- All tasks scheduled within a zone will execute within that context
- Provides hooks into the event loop
Zones
// Requires Node 0.12 or earlier.
require('zone').enable();
zone
.create(function () {
startTimer();
a();
setTimeout(b, 0);
c();
})
.setCallback(function () {
stopTimer();
});
Using StrongLoop zone which is no longer maintained and requires Node.js 0.12 or earlier.
Thank you for listening!
Any questions?
Breaking the async boundary
By James Allardice
Breaking the async boundary
An exploration of the problems caused by ECMAScript's event loop and some of the possible solutions to those problems.
- 1,183