Skip to content

Commit 1e7ef3e

Browse files
authored
[V3] Migrating to AsyncLocalStorage. (#13)
1 parent f628695 commit 1e7ef3e

File tree

23 files changed

+516
-324
lines changed

23 files changed

+516
-324
lines changed

.github/workflows/node.js.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616

1717
strategy:
1818
matrix:
19-
node-version: [10.x, 12.x, 14.x]
19+
node-version: [8.x, 10.x, 12.x, 14.x]
2020

2121
steps:
2222
- uses: actions/checkout@v2

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# CHANGELOG
22

3+
## 3.0.0 (January 03, 2021)
4+
5+
- Introduces `AsyncLocaleStorage` based implementation for node `12.7.0` and above.
6+
- `AsyncHooksContext` domains are now created by default under the hood and no longer require consumers to supply a name.
7+
- `update` API changed to `set`.
8+
- `create` API no longer treats`context` as JS objects.
9+
310
## 2.0.8 (December 20, 2020)
411

512
- Better reporting, safe child getters.

README.md

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# node-execution-context
2-
A straightforward library that provides a persistent process-level context wrapper using node "async_hooks" feature.
2+
A straightforward library that provides a persistent process-level context wrapper using node "async_hooks" feature.
3+
This library will try to use by default [`AsyncLocalStorage`](https://nodejs.org/api/async_hooks.html#async_hooks_class_asynclocalstorage) implementation based if current node version supports it, otherwise it will fallback to raw [`async_hooks`](https://nodejs.org/api/async_hooks.html) implementation for lower versions which mimics this behaviour.
34

45
## Installation
56

@@ -21,7 +22,7 @@ const app = express();
2122
const port = 3000;
2223

2324
const ContextMiddleware = (req, res, next) => {
24-
Context.run(next, { val: true });
25+
Context.run(next, { reference: Math.random() });
2526
};
2627

2728
app.use('/', ContextMiddleware);
@@ -44,7 +45,7 @@ export class UserController {
4445
async create (req) {
4546
const { user } = req.body;
4647

47-
// This will return the reference number set by out ContextMiddleware
48+
// This will return the reference number set by out ContextMiddleware (generated by Math.random())
4849
const { reference } = Context.get();
4950

5051
logger.info('Created user for reference: ', reference);
@@ -56,48 +57,83 @@ export class UserController {
5657

5758
## API
5859

59-
### create(initialContext?: object, domain? :string)
60+
### run(fn: Function, context: unknown)
6061

61-
Creates for the current async resource an execution context entry identified with his asyncId.
62-
Any future processes that will be added to the async execution chain will be exposed to this context.
62+
Runs given callback that will be exposed to the given context.
63+
The context will be exposed to all callbacks and promise chains triggered from the given `fn`.
6364

64-
> When passing custom domain to this method, the trigger point and all of it's sub processes will be exposed to a standalone context and won't effect / be effected by root context.
65+
### get()
6566

66-
### update(update: object)
67+
Gets the current asynchronous execution context.
6768

68-
Updates the current execution context with a given update obect.
69+
> The `get` result is returned by `reference` meaning if you wish any immutability applied, it will have to be manually applied.
6970
70-
### get()
71+
> This API may throw CONTEXT_DOES_NOT_EXIST error if accessed without initializing the context properly.
72+
73+
### set(context: unknown)
74+
75+
Sets the current asynchronous execution context to given value.
76+
77+
> This API may throw CONTEXT_DOES_NOT_EXIST error if accessed without initializing the context properly.
78+
79+
### create(context?: unknown)
80+
81+
Creates a given context for the current asynchronous execution.
82+
It is recommended to use the `run` method. This method should be used in special cases in which the `run` method cannot be used directly.
83+
84+
> Note that if this method will be called not within a AsyncResource context, it will effect current execution context and should be used with caution.
85+
86+
#### Example
87+
88+
```js
89+
const Context = require('node-execution-context');
90+
91+
// context-events.js
92+
const context = { id: Math.random() };
7193

72-
Returns the current execution context identified with the current asyncId.
94+
emitter.on('contextual-event', () => {
95+
Context.create(context);
96+
});
7397

74-
### run(fn: Function, initialContext: object)
98+
// service.js
99+
emitter.on('contextual-event', () => {
100+
Context.get(); // Returns { id: random }
101+
});
75102

76-
Runs a given function under a dedicated AsyncResource, exposing given initial context to the process and it's child processes.
103+
// main.js
104+
emitter.emit('contextual-event');
105+
106+
Context.get(); // Returns { id: random }
107+
```
77108

78109
### configure(config: ExecutionContextConfig) : void
79110

80111
Configures execution context settings.
81112

113+
> Relevant only for node versions lower than `v12.17.0`.
114+
82115
### monitor(): ExecutionMapUsage
83116

84117
Returns an monitoring report over the current execution map resources
85118

119+
> Relevant only for node versions lower than `v12.17.0`.
120+
86121
> Before calling `monitor`, you should `configure` execution context to monitor it's nodes. by default the data kept is as possible.
87122
123+
#### Example
124+
88125
```js
89126
const Context = require('node-execution-context');
90127

91128
// Startup
92129
Context.configure({ monitor: true });
93130

94-
95131
// Later on
96132
const usage = Context.monitor();
97133
console.log(usage); // Prints execution context usage report.
98134
```
99135

100-
### API Usage
136+
### Raw API Usage
101137

102138
```js
103139
const Context = require('node-execution-context');
@@ -109,26 +145,26 @@ Context.create({
109145
Promise.resolve().then(() => {
110146
console.log(Context.get()); // outputs: {"value": true}
111147

112-
Context.update({
148+
Context.set({
113149
value: false
114150
});
115151

116152
return new Promise((resolve) => {
117153
setTimeout(() => {
118154
console.log(Context.get()); // outputs: {"value": false}
119155

120-
Context.update({
156+
Context.set({
121157
butter: 'fly'
122158
});
123159

124160
process.nextTick(() => {
125-
console.log(Context.get()); // outputs: {"value": false, "butter": 'fly'}
161+
console.log(Context.get()); // outputs: {"butter": 'fly'}
126162
resolve();
127163
});
128164

129165
}, 1000);
130166

131-
console.log(Context.get()); // outputs: {"value": true}
167+
console.log(Context.get()); // outputs: {"value": false}
132168
});
133169
});
134170
```
@@ -137,7 +173,6 @@ The following errors can be thrown while accessing to the context API :
137173

138174
| Code | When |
139175
|-|-
140-
| CONTEXT_ALREADY_DECLARED | When trying to `create` execution context, but current async resource already exists.
141-
| CONTEXT_DOES_NOT_EXISTS | When try to `get` / `update` the context, but it yet been created.
176+
| CONTEXT_DOES_NOT_EXIST | When attempting to `get` / `set` a context, that has not yet been created.
142177
| MONITOR_MISS_CONFIGURATION | When try to `monitor` without calling `configure` with monitoring option.
143178

example/controller.js

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,22 +8,24 @@ class UserController {
88
get(req, res) {
99

1010
delay(() => {
11-
console.log('Callback : ', Context.get()); // { val: true }
11+
console.log('Callback1: ', Context.get()); // Callback: { val: true }
1212
});
1313

14-
// Creates a dedicate domain context ( exclude this following chain from root context )
15-
// Updates mae from domain will not effect root context.
16-
delay(() => {
17-
Context.create({ specific: true }, 'custom-domain');
14+
// Creates another context under Timeout AsyncResource un effected/effecting from current one.
15+
setTimeout(() => {
16+
Context.create({ value: 'domain' });
17+
console.log('Domain: ', Context.get()); // Promise: { val: 'domain' }
1818

1919
delay(() => {
20-
console.log('Domain callback ', Context.get()) // { val: true, specific: true }
21-
Context.update({ inner: true });
20+
console.log('Domain Callback: ', Context.get()) // Promise: { val: 'domain' }
21+
}, 3000);
22+
}, 300);
2223

23-
}, 400);
24-
}, 4000)
24+
delay(() => {
25+
console.log('Callback2: ', Context.get()); // Callback: { val: true }
26+
}, 500);
2527

26-
res.send(Context.get());
28+
res.send(Context.get()); // { val: true }
2729
}
2830
}
2931

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "node-execution-context",
3-
"version": "2.0.8",
3+
"version": "3.0.0",
44
"description": "Provides execution context wrapper for node JS, can be used to create execution wrapper for handling requests and more",
55
"author": "Oded Goldglas <[email protected]>",
66
"license": "ISC",

src/ExecutionContext/constants.js

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
* @type {Object<String>}
44
*/
55
exports.ExecutionContextErrors = {
6-
CONTEXT_ALREADY_DECLARED: 'Execution context is already declared for the given domain, use the domain option to create a separate context.',
7-
CONTEXT_DOES_NOT_EXISTS: 'Execution context does not exists, please ensure to call create/run before.',
6+
CONTEXT_DOES_NOT_EXIST: 'Execution context does not exists, please ensure to call create/run before.',
87
MONITOR_MISS_CONFIGURATION: 'Monitoring option is off by default, please call `configure` with the proper options.'
98
};
109

@@ -15,9 +14,3 @@ exports.ExecutionContextErrors = {
1514
exports.DEFAULT_CONFIG = {
1615
monitor: false
1716
};
18-
19-
/**
20-
* The default domain to create execution context roots under.
21-
* @type {String}
22-
*/
23-
exports.ROOT_DOMAIN = 'ROOT';

0 commit comments

Comments
 (0)