Say we want to write transactions within transactions. We would like to use the same DB client when calling client.query but without the pesky context parameter being passed around to each sub transaction.
But we also don’t want the client to be global. I still want to let my transactions run concurrently and on different clients.
This is the exact problem AsyncLocalStorage solves.
import{AsyncLocalStorage}from"async_hooks";// global counter to know the unique client
leti=0;// simple function to mint a client
// attach a function to print its id
functioncreateClient() {return{i:++i,fn(message: string){console.log(this.i,message);},};}typeClient=ReturnType<typeofcreateClient>;// create a store
conststore=newAsyncLocalStorage();// mock db class
classDB{asyncwith<T>(fn:(client: Client)=>Promise<T>){// get the context
constclient=store.getStore()asClient|undefined;// if i am already in the context reuse the client
if(client){returnawaitfn(client);}else{// if i am not create a client
constclient=createClient();// store.run it!
returnawaitstore.run(client,async()=>{awaitfn(client);});}}}constdb=newDB();asyncfunctiontx1() {db.with(async(client)=>{client.fn("tx1");});}// client 1 created!
tx1();asyncfunctiontx2() {db.with(async(client)=>{// eventhough tx1 creates a new client when called previously
// here it doesn't!
// instead of passing context into tx1() from tx2()
// async local storage does it for us internally
awaittx1();client.fn("tx2");});}// client 2 created!
tx2();