about 3 years ago Syntax Podcast
AsyncLocalStorage + AsyncContext API
- 02:05 AsyncLocalStorage added in Node 13
- 02:13 Gaining momentum due to Async Context API proposal
- 02:38 Allows accessing context down call stack without passing refs
- 03:26 Example of passing context through nested functions
- 04:06 Current way is passing references down call stack
- 04:25 Why frameworks don't use it yet for middleware
- 05:00 Examples in SvelteKit and React
- 06:30 Use case: populating user from database in middleware
- 07:02 Alternative is passing context object with issues
- 08:46 Not in other runtimes yet, needs JS proposal
- 10:18 Next.js uses it for cookies and headers
- 10:46 Use cases: request IDs, user prefs, logging
- 11:39 Example of logging show numbers without passing refs
- 12:59 Can store anything - objects, maps, sets, timers
- 13:33 Useful for promise chaining instead of passing data
- 14:22 Downside is hard to test code relying on external data
- 15:00 Can also cause memory leaks if not cleaned up properly
- 15:55 Useful for browser events instead of passing data around
- 16:43 Expect best practices to emerge over next year
Transcript
Wes Bos
Reactathon,
Scott Tolinski
thing that allows you to
Scott Tolinski
other runtimes, other JavaScript runtimes. Yeah. When did this drop in Node? And
Scott Tolinski
Should we get on into it? We should. And I I will be taking the role of somebody who has not used it. I generally know what it is, but I will be trying to ask you some questions. So if if my question seem dumb
Scott Tolinski
in JavaScript. This is a server side JavaScript Or if they seem, like,
Scott Tolinski
On this Monday, HD Treat, we're gonna be talking about
Scott Tolinski
sync local storage in the async context API
Scott Tolinski
async local storage.
Scott Tolinski
Inside of Node specifically
Scott Tolinski
as well as what these these,
Scott Tolinski
Welcome to Syntex.
Scott Tolinski
So my name is Scott Cholinski. I'm a developer from Denver. And with me as always is Wes
Announcer
Open wide dev fans, get ready to stuff your face with JavaScript, CSS, node modules, barbecue tips, get workflow, breakdancing,
Scott Tolinski
your entire call stack for any given
Announcer
Monday. Monday. Monday.
Announcer
soft skill, web development, the hastiest, the craziest, the tastiest TS web development treats coming in hot. Here is Wes, Barracuda,
Wes Bos
talk, It's not a huge use case, but certainly will be handy. I could see this being handy for events in the browser. So, like, if you have async local storage I specifically have been using one called Hano Jet. Js, into every single one of those functions just to console log it. Right. I'm not using the show number anywhere else.
Scott Tolinski
off based, try to steer me in that direction because I'm interested in this stuff. And I I kinda have an idea of when you would use it, but I I wanna Really get the full picture here. So,
Scott Tolinski
Is it like are all these JavaScript run times aligned on this?
Announcer
key.
Scott Tolinski
piece of information that you want to share in that regard.
Scott Tolinski
My understanding is that this is a new API within JavaScript
Not in other runtimes yet, needs JS proposal
Wes Bos
and that will give you the cookies function internally will reach out
Wes Bos
So let's go into some actual examples
Wes Bos
in Next. Js
Wes Bos
you could pass data from 1 middleware to another because
Scott Tolinski
the actual JavaScript
Scott Tolinski
TC 30 9 proposal to to add this to the language. So you don't you don't see this as being a part of a lot of things until
Wes Bos
I'm assuming with Next. Js, clean it up if you do have that issue, though.
Wes Bos
every single function that needs it. You can simply just access it wherever it is that you need it. So I think we are starting to see This type of thing being implemented. Again, it's only 2 or 3 years old. I guess that is kind of long, but we're starting to see a lot of people glad to share, and we'll catch you later. Peace. Peace.
Alternative is passing context object with issues
Scott Tolinski
aren't using async local storage
Wes Bos
I don't want to like, why are we using just a node API? So that's why they're not like a stand alone.
Wes Bos
So the and
Wes Bos
but it doesn't work as soon as you Maybe there's a potential memory leak there. There is an exit method you can do that will explicitly Some foot guns. Having things globally available I have done a I did a little TikTok on it a couple of weeks ago, and then I talked about it in my And you want to be able to access the headers or the cookies, the level, you'll be able to access
Scott Tolinski
Are you raising your hand? I'm raising my hand. Oh, what's up? I would like I would just have a quick question. I wanna I just wanna interrupt. I wanted to get this before you moved on from this.
Scott Tolinski
Why would you say that most
Wes Bos
A lot of these frameworks are trying to within Next. Js. So if you have an action or a and then anywhere else I work to console. Log, I can simply just access it. In fact, I made a custom console log function, down the line, a middleware is a really good example where You might have a middleware that, like, populates your user.
Scott Tolinski
frameworks today in node
Wes Bos
new API
Scott Tolinski
context,
Wes Bos
a lot of frameworks these days, One of the reasons is that this is so this is a Node API.
Scott Tolinski
for
Wes Bos
anywhere down specifically, this API doesn't cause memory leaks, but it's just another thing to think about if you are referencing an object or value Has to then be past it, right? You might have context that like a top level, but what happens if you want it 7 levels deep in your data fetching library, right? You don't necessarily have access to So the async local storage will allow you to define a store and access it anywhere down. It's sort of like I kind of look at it as a closure variable. In JavaScript. If you have declare a variable inside of a function
Downside is hard to test code relying on external data
Wes Bos
that context
Wes Bos
is makes it kind of hard to test, right? Think about if you want to test a React component, Promise chaining each. If you use .then We have this problem in React and many other, and access the sync local search, right? You can see how that could be a bit of an issue. Yeah.
Wes Bos
And then it could cause a memory leak as well. Just like anything in JavaScript, if you are referencing something That is it.
Wes Bos
your function needs to reach into the ether and tracing console logs. That's where 98% of this type of thing is going to lie, where you need to be able to access it.
Example of logging show numbers without passing refs
Wes Bos
because You could grab it A cyclical surge is pretty cool. So we're gonna kind of run through what it is a function that calls another function, right? And that function will say, Okay, let's probably the most common use case is for logging and tracing of request IDs. Every single time you have a request come in, You can make a unique identifier for that request Boss, and Scott you're on the first one, you have a request ID and then the 2nd time within my route handler inside of a function
Wes Bos
the whole thing is garbage collected and you don't have to worry about it anymore.
Wes Bos
The API for it, you can stick anything you want in it. So it's not like local storage where it's a key value. It could be a variable,
Wes Bos
I can say, okay, well, I know that this happened on this request
Wes Bos
When the function starts and then when it's done, in the summarize it. We have a function but a store what context is. So when you have a function in JavaScript and you call that function. So like, let's say somebody's going to westboss.com.
Wes Bos
We put the show number in the synclical storage
Gaining momentum due to Async Context API proposal
Wes Bos
for something called the Async Context API, which does pretty much the exact same thing, which means that Or on this specific show parsing, you might be saying, well, can't you just stick a variable in memory? You know, just make a variable outside your function and reference that on every single time. The problem with that is if you're running 2 functions at once, when I was
Examples in SvelteKit and React
Scott Tolinski
the the prop flow of things inside of React. Right? Exactly.
Scott Tolinski
avoid that problem of having to pass props from child to child to child to child. You put something in the React context and are able to access it outside of the,
Wes Bos
what we're doing right now in a lot of use cases is we have a request object.
Wes Bos
And if you want something to be able to be accessed work on every single object.
Wes Bos
8 levels deep, but I don't want to have to manually pass it along
Wes Bos
within that function call so that any functions inside of it that reach out will be able to access it. So tracing.
Scott Tolinski
The other one might just be in general React context where, Let's say you wanted to
Scott Tolinski
essentially
Wes Bos
every single way, and we'll talk about the benefits and cons against that in just a sec as well. So those variables in memory are often shared and they're overriding each other. This will simply just make a store proposal gets pushed through. That's a good question. But, so Next. Js access to data that is in context.
Wes Bos
and access that store if you have multiple middlewares, it has been gaining a little bit of momentum recently because of a few reasons.
Example of passing context through nested functions
Wes Bos
fetch some excited to talk about it.
Wes Bos
all the way down. You know, a function calls a function that calls another function. If you ever want to share data between So obviously they polyfilled it and you can use it in Deno. But there is a proper,
Wes Bos
but that function might call another function. That function might call another function. Right.
Wes Bos
I'll have a route handler. That's a function, right? And that function will get a request and then we can send a response back from it. Right. And then in that handler, that increases memory usage. And eventually, you're going to run out of memory and your app will fall over. So server function or anything Find this useful because you can also, and then any function that is called anywhere down With a sync local storage, prop based languages where
Wes Bos
So any
Wes Bos
I might have people saw this in notes, Hey, that's actually kind of helpful.
Useful for browser events instead of passing data around
Wes Bos
An event that fires, So this dropped in Node 13. So meaning it's been around for 3 years already in Node.
Scott Tolinski
application and stuff using the the context. I don't need to rewrite things, but, like, it seems like that's, like, what I would want to do. Just throw it in a async local storage within my my server side hook.
Wes Bos
and be able to access that find a function somewhere else.
Scott Tolinski
Yeah. And then or somebody like you, sharing this. Awesome. Alright. Well,
Wes Bos
in sync context But that React component assumes that it has It's not being garbage collected. And every single time you make an object of people, balls. Hey, For some use cases,
Scott Tolinski
somewhere else. It feels like I want to do that in my
Wes Bos
be able to access it application wide. Sounds pretty neat. Exactly. Like it's similar to like a request object, right? Request comes in. You want to be able to access that data, then maybe an event.
Why frameworks don't use it yet for middleware
Scott Tolinski
you have like
Scott Tolinski
locals is just available, and everything that gets saved to locals is available. But it is passed through the function.
Scott Tolinski
In in Svelte kits, for instance, a request comes in
Scott Tolinski
Yeah. Or I wasn't gonna say React content. This is something different. This is, were you thinking React contacts? Is that what you were implying? Both those. I think you should go into both of them. So in middleware, typically, a request comes in maybe like an auth token or something. You validate that user, and then you pass that user information along. In SvelteKit, they do it through something called locals where in all of your request functions,
Can store anything - objects, maps, sets, timers
Wes Bos
Some. I guess sometimes it makes sense. Just pass that ID around. But if you want to be able to clear the timer, there's an API for starting it with existing data.
Wes Bos
the timer in another function.
Wes Bos
could be an object. A lot of times people use sets and maps because sets and maps have nice APIs to work with them. You can do whatever you want. You can stick timer IDs in there. Like, Let's say you start a timer in 1 function and then you want to be able to clear
Use case: populating user from database in middleware
Wes Bos
It's kind of just a spot where you stick anything you want. Anybody can stick anything on there, However,
Wes Bos
And so the request comes in.
Wes Bos
call it a context
Wes Bos
You populate the user, you get them from the database, and then you stick the whole user on the request object and you call next or you return it. And then the function that gets called next down the line will we'll have access to that because it's on the request and kind of the request object or a lot of frameworks You could just stick it sometimes if you reference something in JavaScript that is and the console log function won't reach into a link, async local storage, grab the show number, and then prepend the show number with the console log. So my console logs are not a huge mess.
Next.js uses it for cookies and headers
Wes Bos
and use it to access it anywhere down the line. So you could put a cookie in there. We've said you could use logging, and bring it down for that. That makes a lot of sense because you don't have to pass it along to every single function, and it's really nice. So instead of passing the data every single function that does want to be able to access the context I foresee us Seeing like best practices come out of this in the next year or so of like people, Oh, we used a sync local storage everywhere and the thing blew up. Or here's a couple of use cases. We did use sync local storage, and it worked out well for us. Very fascinating. Well, cool stuff, Wes. This is the type of stuff that I, personally, you know, wouldn't come across it if it wasn't
Wes Bos
of like, why would you ever want to use this as well as like, isn't this a bad idea into the sky, Grab the async local storage value down and return to you all the cookies. That's super nice because, again, you don't have to pass the headers and the cookies by its name, Only using a node API on on out from that. So running all the Syntax
Allows accessing context down call stack without passing refs
Wes Bos
let's understand is kind of a no go for a lot of them. I went into I was like, Okay, is this in Deno? And I literally found the thread where Ryan Dahl goes, This is cool. I can see where this would be helpful, which was on, like, next gen JavaScript frameworks.
Wes Bos
It will allow you to access context down the call stack. So before we get into any of these things, and .catch syntax instead of asyncawait, condense it. We have a function to You're going to have the same issues here where your test runner will have to mock So I think local storage. It's kind of like a bad name because it doesn't have anything to do with local storage.
Wes Bos
It doesn't have an API like local storage, really. It doesn't it's not like it's not like, oh, it's like it's like local storage, but you can async away to it. It's nothing like that.
Wes Bos
browser as well? you simply just import a function from Next. Js library called cookies. You run cookies and and why you might need it.
Use cases: request IDs, user prefs, logging
Wes Bos
episodes through the OpenAI API.
Wes Bos
one zero seven for show number 203. Right. But I don't want to pass the show number
Wes Bos
So why do I have to pass it in just for my own logging needs? Well, when you start off that initial request, you could just reach into a sync local storage, get the timer ID, Go ahead and clear it.
Wes Bos
You for middleware. Yeah. That's a great question. I think because it is a relatively you could just stick prefixed with the show number. And if there's an error,
Wes Bos
could query user preferences to add the speaker, Right? Like, there's probably 20 different functions that are running, and I want a console log running x, y, and z for show number when
Wes Bos
It's pretty nice for that type of stuff. I specifically
Wes Bos
and just be able to get your users preferences wherever it is that you want.
Current way is passing references down call stack
Wes Bos
you pass something along from function to function so that anybody that needs it anywhere down the line will be able to have access to it. Sound familiar? We have this problem.
Wes Bos
Where where have you seen this problem before? called deploy target ever. Oh, I see. So using
Wes Bos
the way to do it is you just pass some sort of reference or has implemented it. Cloudflare Workers has implemented it, and Next. Js uses it to be able to access cookies and headers The the call stack or any function that is then subsequently and everywhere? So let's explain. In client side JavaScript. Yeah, Yeah, exactly. Like, I have a couple of examples of, like, why would you ever want this of that event all of those function calls.
Wes Bos
Right now, They're just nicely You have like a that might overwrite each other, because if things happen out of order, then you get into real trouble those are some kind of use cases. I think in most cases, a closure variable, there is now a proposal in TC39 all the way down, It's not a very pure function a function that is called I want something to go from 1 level down will be able to essentially just reach into the thin air
Scott Tolinski
Middleware. So, basically have a context amongst Head on over to syntax.fm
Useful for promise chaining instead of passing data
Wes Bos
each piece of data that you get into a sync local storage and then when it's all done, values from the database, Will allow you to define and you no longer reference it, JavaScript will garbage collect it. However,
Wes Bos
Sometimes it's annoying to Yeah. You have to if you want like data shared between the 2, you have to like pass it. You have to return it from the then and then destructure it in the next then or you have to like, make a variable outside that's empty and then reach outside and update it right. So Wouldn't it be cool if that was just a feature we had in JavaScript user sessions, user login
Expect best practices to emerge over next year
Scott Tolinski
Or drop a review if you like this show.
Scott Tolinski
for a full archive of all of our shows.
Scott Tolinski
And don't forget to subscribe in your podcast player
Can also cause memory leaks if not cleaned up properly
Wes Bos
like a variable somewhere else you would think, oh, yeah, JavaScript will garbage collect it. You can run into an issue where I have like we have a function to
AsyncLocalStorage added in Node 13
Wes Bos
However, you got to think about, okay, well, And I was firing off 5 at a time and and it's really hard to type it. It's really hard to know what's available to yourself.