Last active
July 24, 2026 05:28
-
-
Save oriSomething/6c156db0069a595be60db1d071dda438 to your computer and use it in GitHub Desktop.
slow partial async context support in the browser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // 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