@@ -163,23 +163,19 @@ logically-connected sync/async code execution.
163
163
namespace AsyncContext {
164
164
class Variable <T > {
165
165
constructor (options : AsyncVariableOptions <T >);
166
-
167
166
get name(): string ;
168
-
169
167
run<R >(value : T , fn : (... args : any [])=> R , ... args : any []): R ;
170
-
171
168
get(): T | undefined ;
172
169
}
173
-
174
170
interface AsyncVariableOptions <T > {
175
171
name? : string ;
176
172
defaultValue? : T ;
177
173
}
178
174
179
175
class Snapshot {
180
176
constructor ();
181
-
182
177
run<R >(fn : (... args : any []) => R , ... args : any []): R ;
178
+ wrap<T , R >(fn : (this : T , ... args : any []) => R ): (this : T , ... args : any []) => R ;
183
179
}
184
180
}
185
181
```
@@ -299,6 +295,74 @@ runWhenIdle(() => {
299
295
A detailed explanation of why ` AsyncContext.Snapshot ` is a requirement can be
300
296
found in [ SNAPSHOT.md] ( ./SNAPSHOT.md ) .
301
297
298
+ ### ` AsyncContext.Snapshot.wrap `
299
+
300
+ ` AsyncContext.Snapshot.wrap ` is a helper which captures the current values of all
301
+ ` Variable ` s and returns a wrapped function. When invoked, this wrapped function
302
+ restores the state of all ` Variable ` s and executes the inner function.
303
+
304
+ ``` typescript
305
+ const asyncVar = new AsyncContext .Variable ();
306
+
307
+ function fn() {
308
+ return asyncVar .get ();
309
+ }
310
+
311
+ let wrappedFn;
312
+ asyncVar .run (" A" , () => {
313
+ // Captures the state of all AsyncContext.Variable's at this moment, returning
314
+ // wrapped closure that restores that state.
315
+ wrappedFn = AsyncContext .Snapshot .wrap (fn )
316
+ });
317
+
318
+
319
+ console .log (fn ()); // => undefined
320
+ console .log (wrappedFn ()); // => 'A'
321
+ ```
322
+
323
+ You can think of this as a more convenient version of ` Snapshot ` , where only a
324
+ single function needs to be wrapped. It also serves as a convenient way for
325
+ consumers of libraries that don't support ` AsyncContext ` to ensure that function
326
+ is executed in the correct execution context.
327
+
328
+ ``` typescript
329
+ // User code that uses a legacy library
330
+ const asyncVar = new AsyncContext .Variable ();
331
+
332
+ function fn() {
333
+ return asyncVar .get ();
334
+ }
335
+
336
+ asyncVar .run (" A" , () => {
337
+ defer (fn ); // setTimeout schedules during "A" context.
338
+ })
339
+ asyncVar .run (" B" , () => {
340
+ defer (fn ); // setTimeout is not called, fn will still see "A" context.
341
+ })
342
+ asyncVar .run (" C" , () => {
343
+ const wrapped = AsyncContext .Snapshot .wrap (fn );
344
+ defer (wrapped ); // wrapped callback captures "C" context.
345
+ })
346
+
347
+
348
+ // Some legacy library that queues multiple callbacks per macrotick
349
+ // Because the setTimeout is called a single time per queue batch,
350
+ // all callbacks will be invoked with _that_ context regardless of
351
+ // whatever context is active during the call to `defer`.
352
+ const queue = [];
353
+ function defer(callback ) {
354
+ if (queue .length === 0 ) setTimeout (processQueue , 1 );
355
+ queue .push (callback );
356
+ }
357
+ function processQueue() {
358
+ for (const cb of queue ) {
359
+ cb ();
360
+ }
361
+ queue .length = 0 ;
362
+ }
363
+ ```
364
+
365
+
302
366
# Examples
303
367
304
368
## Determine the initiator of a task
0 commit comments