@@ -154,81 +154,83 @@ Non-goals:
154154logically-connected sync/async code execution.
155155
156156``` typescript
157- class AsyncContext <T > {
158- static wrap<R >(callback : (... args : any []) => R ): (... args : any []) => R ;
157+ namespace AsyncContext {
158+ class Variable <T > {
159+ constructor (options : AsyncVariableOptions <T >);
159160
160- constructor ( options : AsyncContextOptions < T >) ;
161+ get name() : string ;
161162
162- get name() : string ;
163+ run< R >( value : T , fn : ( ... args : any []) => R , ... args : any []) : R ;
163164
164- run<R >(value : T , callback : () => R ): R ;
165+ get(): T | undefined ;
166+ }
165167
166- get(): T | undefined ;
167- }
168+ interface AsyncVariableOptions <T > {
169+ name? : string ;
170+ defaultValue? : T ;
171+ }
172+
173+ class Snapshot {
174+ constructor ();
168175
169- interface AsyncContextOptions <T > {
170- name? : string ;
171- defaultValue? : T ;
176+ run<R >(fn : (... args : any []) => R , ... args : any []): R ;
177+ }
172178}
173179```
174180
175- ` AsyncContext .prototype.run()` and ` AsyncContext .prototype.get()` sets and gets
176- the current value of an async execution flow. ` AsyncContext.wrap() ` allows you
177- to opaquely capture the current value of all ` AsyncContext ` s and execute the
178- callback at a later time with as if those values were still the current values
179- (a snapshot and restore). Note that even with ` AsyncContext.wrap() ` , you can
180- only access the value associated with an ` AsyncContext ` instance if you have
181+ ` Variable .prototype.run()` and ` Variable .prototype.get()` sets and gets
182+ the current value of an async execution flow. ` Snapshot ` allows you
183+ to opaquely capture the current value of all ` Variable ` s and execute a
184+ function at a later time with as if those values were still the current values
185+ (a snapshot and restore). Note that even with ` Snapshot ` , you can
186+ only access the value associated with an ` Variable ` instance if you have
181187access to that instance.
182188
183189``` typescript
184- const context = new AsyncContext ();
190+ const asyncVar = new AsyncContext . Variable ();
185191
186192// Sets the current value to 'top', and executes the `main` function.
187- context .run (" top" , main );
193+ asyncVar .run (" top" , main );
188194
189195function main() {
190- // Context is maintained through other platform queueing.
196+ // AsyncContext.Variable is maintained through other platform queueing.
191197 setTimeout (() => {
192- console .log (context .get ()); // => 'top'
198+ console .log (asyncVar .get ()); // => 'top'
193199
194- context .run (" A" , () => {
195- console .log (context .get ()); // => 'A'
200+ asyncVar .run (" A" , () => {
201+ console .log (asyncVar .get ()); // => 'A'
196202
197203 setTimeout (() => {
198- console .log (context .get ()); // => 'A'
204+ console .log (asyncVar .get ()); // => 'A'
199205 }, randomTimeout ());
200206 });
201207 }, randomTimeout ());
202208
203- // Context runs can be nested.
204- context .run (" B" , () => {
205- console .log (context .get ()); // => 'B'
209+ // AsyncContext.Variable runs can be nested.
210+ asyncVar .run (" B" , () => {
211+ console .log (asyncVar .get ()); // => 'B'
206212
207213 setTimeout (() => {
208- console .log (context .get ()); // => 'B'
214+ console .log (asyncVar .get ()); // => 'B'
209215 }, randomTimeout ());
210216 });
211217
212- // Context was restored after the previous run.
213- console .log (context .get ()); // => 'top'
218+ // AsyncContext.Variable was restored after the previous run.
219+ console .log (asyncVar .get ()); // => 'top'
214220
215- // Captures the state of all AsyncContext's at this moment.
216- const snapshotDuringTop = AsyncContext .wrap ((cb ) => {
217- console .log (context .get ()); // => 'top'
218- cb ();
219- });
221+ // Captures the state of all AsyncContext.Variable's at this moment.
222+ const snapshotDuringTop = new AsyncContext .Snapshot ();
220223
221- // Context runs can be nested.
222- context .run (" C" , () => {
223- console .log (context .get ()); // => 'C'
224+ asyncVar .run (" C" , () => {
225+ console .log (asyncVar .get ()); // => 'C'
224226
225- // The snapshotDuringTop will restore all AsyncContext to their snapshot
226- // state and invoke the wrapped function. We pass a callback which it will
227+ // The snapshotDuringTop will restore all AsyncContext.Variable to their snapshot
228+ // state and invoke the wrapped function. We pass a function which it will
227229 // invoke.
228- snapshotDuringTop (() => {
230+ snapshotDuringTop . run (() => {
229231 // Despite being lexically nested inside 'C', the snapshot restored us to
230232 // to the 'top' state.
231- console .log (context .get ()); // => 'top'
233+ console .log (asyncVar .get ()); // => 'top'
232234 });
233235 });
234236}
@@ -238,7 +240,7 @@ function randomTimeout() {
238240}
239241```
240242
241- ` AsyncContext.wrap ` is useful for implementing APIs that logically "schedule" a
243+ ` Snapshot ` is useful for implementing APIs that logically "schedule" a
242244callback, so the callback will be called with the context that it logically
243245belongs to, regardless of the context under which it actually runs:
244246
@@ -247,7 +249,8 @@ let queue = [];
247249
248250export function enqueueCallback(cb : () => void ) {
249251 // Each callback is stored with the context at which it was enqueued.
250- queue .push (AsyncContext .wrap (cb ));
252+ const snapshot = new AsyncContext .Snapshot ();
253+ queue .push (() => snapshot .run (cb ));
251254}
252255
253256runWhenIdle (() => {
@@ -261,11 +264,11 @@ runWhenIdle(() => {
261264```
262265
263266> Note: There are controversial thought on the dynamic scoping and
264- > ` AsyncContext ` , checkout [ SCOPING.md] [ ] for more details.
267+ > ` Variable ` , checkout [ SCOPING.md] [ ] for more details.
265268
266269## Use cases
267270
268- Use cases for ` AsyncContext ` include:
271+ Use cases for async context include:
269272
270273- Annotating logs with information related to an asynchronous callstack.
271274
@@ -300,7 +303,7 @@ A detailed example usecase can be found [here](./USE-CASES.md)
300303## Determine the initiator of a task
301304
302305Application monitoring tools like OpenTelemetry save their tracing spans in the
303- ` AsyncContext ` and retrieve the span when they need to determine what started
306+ ` AsyncContext.Variable ` and retrieve the span when they need to determine what started
304307this chain of interaction.
305308
306309These libraries can not intrude the developer APIs for seamless monitoring. The
@@ -309,20 +312,20 @@ tracing span doesn't need to be manually passing around by usercodes.
309312``` typescript
310313// tracer.js
311314
312- const context = new AsyncContext ();
315+ const asyncVar = new AsyncContext . Variable ();
313316export function run(cb ) {
314317 // (a)
315318 const span = {
316319 startTime: Date .now (),
317320 traceId: randomUUID (),
318321 spanId: randomUUID (),
319322 };
320- context .run (span , cb );
323+ asyncVar .run (span , cb );
321324}
322325
323326export function end() {
324327 // (b)
325- const span = context .get ();
328+ const span = asyncVar .get ();
326329 span ?.endTime = Date .now ();
327330}
328331```
@@ -358,20 +361,20 @@ concurrent multi-tracking.
358361
359362## Transitive task attribution
360363
361- User tasks can be scheduled with attributions. With ` AsyncContext ` , task
364+ User tasks can be scheduled with attributions. With ` AsyncContext.Variable ` , task
362365attributions are propagated in the async task flow and sub-tasks can be
363366scheduled with the same priority.
364367
365368``` typescript
366369const scheduler = {
367- context : new AsyncContext (),
370+ asyncVar : new AsyncContext . Variable (),
368371 postTask(task , options ) {
369372 // In practice, the task execution may be deferred.
370- // Here we simply run the task immediately with the context .
371- return this .context .run ({ priority: options .priority }, task );
373+ // Here we simply run the task immediately.
374+ return this .asyncVar .run ({ priority: options .priority }, task );
372375 },
373376 currentTask() {
374- return this .context .get () ?? { priority: " default" };
377+ return this .asyncVar .get () ?? { priority: " default" };
375378 },
376379};
377380
0 commit comments