Skip to content

Instantly share code, notes, and snippets.

@oriSomething
Last active July 24, 2026 05:28
Show Gist options
  • Select an option

  • Save oriSomething/6c156db0069a595be60db1d071dda438 to your computer and use it in GitHub Desktop.

Select an option

Save oriSomething/6c156db0069a595be60db1d071dda438 to your computer and use it in GitHub Desktop.
slow partial async context support in the browser
//
// Infrastructure to emualate partially async context in browser
//
// Every context that can be used in parallel by id
const contexts = new Map();
// Getting the context
function getContext() {
// We extract the context id of the context id from the stack trace.
// 1. It's slow
// 2. It's limited by `Error.stackTraceLimit`
// 3. It's only supports what async stack trace support. So no trace inside
// `setTimout` callback for example
// I use `password-*-password` named function to make it easy to trace
const re = /password-(\d+)-password/;
const { stack } = new Error();
const contextId = stack.match(re)?.[1];
return contexts.get(contextId);
}
// The function that allow run the action with context
async function run(action, { context }) {
// We create an id for storing thw context
const contextId = ((Math.random() * 10_000) | 0).toString(10);
// And store it
contexts.set(contextId, context);
// We make sure it would be easy to extract the `contextId` from stack trace
// by creating a named function as a wrapper
const methodName = `password-${contextId}-password`;
const runner = {
async [methodName]() {
return await action();
},
}[methodName];
try {
await runner();
} finally {
// When action is finished we delete the stored context regardless success
// or failure
contexts.delete(contextId);
}
}
///
/// Actual usage
///
async function action(id) {
console.log(id, getContext());
await new Promise((resolve) => setTimeout(resolve, 100));
// If it wouldn't `getContext` would return wrong result here for one fo the
// calls
console.log(id, getContext());
await new Promise((resolve) => setTimeout(resolve, 100));
console.log(id, getContext());
await new Promise((resolve) => setTimeout(resolve, 100));
}
// We must run in parallel to show it works
await Promise.all([
run(action.bind(undefined, 1), { context: "a" }),
run(action.bind(undefined, 2), { context: "b" }),
]);
// console output:
// ===============
// 1 'a'
// 2 'b'
// 1 'a'
// 2 'b'
// 1 'a'
// 2 'b'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment