proposal-async-context icon indicating copy to clipboard operation
proposal-async-context copied to clipboard

[name bikeshed] This API is useful in both async and sync situations

Open pygy opened this issue 2 years ago • 8 comments

This is IMO a much needed addition to the language, I love this proposal.

I wonder however if calling it AsyncContext is the best way to go about it, given that it is also useful in sync contexts, and could replace the try{}finally{} pattern described in the README in every situation.

While we're at it, given that this is already the context in JS, another term may be less confusing. I know that React also uses context for its dynamic scope construct, so there's also a precedent here... No strong opinions, just raising this in case we find something better.

pygy avatar Feb 02 '23 15:02 pygy

We'll take this under advisement. I think @michaelficarra also suggested a few names?

I wonder however if calling it AsyncContext is the best way to go about it, given that it is also useful in sync contexts, and could replace the try{}finally{} pattern described in the README in every situation.

I wouldn't advise this. While it's usable in sync contexts, the API is going to have some overhead during run/get operations. We're going to try to make this fast, but if you could use another solution, it's likely that whatever that is will be faster overall.

jridgewell avatar Feb 02 '23 17:02 jridgewell

In many contexts, cleanliness trumps absolute perf though, and this API with a name that doesn't include "async" could convey the intention better than the try{}/finally{} pattern.

There are other examples already in the language, like for/of loops over arrays that are slower but neater than for(;;).

pygy avatar Feb 02 '23 20:02 pygy

for-of is not necessarily slow , via JIT engines could optimize it to have the same perf to for(;;). So the problem is whether engines can optimize AsyncContext in sync usage?

hax avatar Mar 23 '23 19:03 hax

That's a good point. Indeed, in Safari, once the best JIT kicks in for/of becomes faster than for(;;).

pygy avatar Mar 24 '23 02:03 pygy

There's a subtle difference when using this API in a synchronous way compared to try {} finally {}:

let normalVar = undefined;
const asyncVar = new AsyncContext.Variable();

// prints 123
asyncVar.run(123, () => {
  setTimeout(() => console.log(asyncVar.get()), 1000);
});

// prints undefined
const oldNormalVar = normalVar;
normalVar = 123;
try {
  setTimeout(() => console.log(normalVar), 1000);
} finally {
  normalVar = oldNormalVar;
}

The name being AsyncContext (as compared to just Context) makes this behavior clear, particularly that anything asynchronous scheduled inside will have the same context. (A real example where this matters is if you want to implement something like console.group, or if you want RAII while ensuring the resource can't be accessed beyond its lifetime.)

I find the second (synchronous-only) solution common enough that SyncContext alongside AsyncContext would be nice to have (and it would help explaining the feature), but this can be implemented as a library in today's JavaScript already.

const syncVar = new SyncContext.Variable();

// prints undefined (or throws an error)
syncVar.run(123, () => {
  setTimeout(() => console.log(syncVar.get()), 1000);
});

N2D4 avatar Jan 21 '24 23:01 N2D4

One name already used in the ecosystem is "continuation local storage" which is nice since it isn't specific to sync or async. See for example https://www.npmjs.com/package/continuation-local-storage with ~2m weekly downloads.

That's also used by some popular libraries directly (e.g. Sequelize) so there's some familiarity with it.

Yona-Appletree avatar Mar 08 '24 19:03 Yona-Appletree

I would actually suggest against reusing an existing name as it may result in confusion between the two. If we really want something agnostic of sync versus async, perhaps something like ExecutionContext? The core intent of it is modelling execution flow regardless of if that is sync or async. Notably, the context is immediately available in the run function which runs synchronously, not only available within nested async functions.

Qard avatar Mar 08 '24 19:03 Qard

This name AsyncContext has actually become a bit confusing as I've started to pitch reusing this mechanism to the web framework community, for other things that are commonly called "context" and, similarly, boil down to saving and restoring variable maps along the callstack for later execution. But I'm not sure what a better name is... at this point, the AsyncContext name has been shared in a lot of contexts and seems to be "sticking".

littledan avatar Apr 08 '24 21:04 littledan