I can't believe it, but I actually understood the code from 0:50 without explanation from the video. Needed to sneak a peek at core-js's implementation of setImmediate tho. My basic understanding was: prerender is a function that can take some time to run or might not, so using setImmediate we can stop "rendering" and get intermediate result from synchronous part of rendering process.
this is my interpretation: prerender has async inside, setImmediate puts a callback before any async tasks so after prerender completes with every sync task and moves to async tasks the setImmediate callback is executed which aborts the async tasks via the abort controller and resolves all the non-async prerendered react stuff
Yes, that part looked like a step back, but things start to make sense again a few seconds later, when he moves all the code into that new async Content component.
So, i paused it where you showed it. This is what I found. This code shows how to manage server-side rendering with React using prerender and an AbortController. The prerender function renders a React component () on the server, and the AbortController allows you to cancel the rendering if it takes too long. A promise is used to handle the process, ensuring the result is either successfully completed or an error is caught if something goes wrong. Two setImmediate calls are used to control the flow. The first tries to render the component and handles any errors. The second cancels the render using the AbortController and resolves the promise with the result, even if the process was stopped early. This setup ensures rendering doesn't hang and that server resources are used efficiently. It's helpful for managing timeouts and handling errors in server-side rendering. Lets see how close I am.
The moment I saw those setImmediate and controller abort, I assumed there was some funny prevent async loops to get kicked in shenanigans. This is such a clever way of using JavaScript's (or in this case, Node.js's Event Loop)
Without dynamicIO (and no PPR enabled) the pages are either completely static or dynamic. PPR in Next.js 14 enabled the combination of static and dynamic parts on the same page, and dynamicIO builds on this concept while also adding "use cache" to the mix.
What I'm surprised about it how prerender doesn't fail when the promises it listens to are not responded to. I'm guessing that's what the AbortController does (rejecting all invoked promises) but it's impressive that prerender can figure out how to handle what to do about components which don't resolve (maybe it just returns blank for those, idk)
10:45 Question about the TopNav (which has the "use client"). I follow when you say that's static and should be rendered ASAP, how does the client side js work out? I mean what if we are using client data (AV info, date time, etc)? Where does that fall into the event loop and stack or whatever?
So, simple question. Why do you just do a static HTML page with all the placeholders and state by default and then bring in dynamic content in every case? In essence, force the developers to make as much of the site static as possible. Take the pokemon page. Make it static with the default greyed out pokemon choices that boom, get replaced with non-greyed out new choices when they are ready (or when the pick new random button is chosen). Also, why do components not require static placeholder content? I am thinking back to the old days of UI builders, when you brought in a component, it'd be run "in design time" and the content was often just a placeholder that you could still see how to fit into the app, but with faked or blank data. In that case, just have the framework be able to spit out a static rendered site to preview the design as well as the placeholder static HTML until all the scripts run and actual content is ready with just a flag? Or are developers so obsessed with not repeating themselves that making a bit of mocked up HTML to give an idea as what is to come as well as the real thing is just too much to handle? ETA: I don't think the above model is inconsistent with server side rendering in general. After all, nothing says that if you have a page with some components, you have then have to construct all the HTML on the client. The components may happily serve back the HTML to be placed inside the page, I would imagine.
Yes this. The video doesn't quite explain why anyone would allow a slow component that takes more than 2 seconds to block everything server side and not just turn that into a client side component that fetches data from the client. Maybe there is a reason but this didn't explain why this problem exists to begin with.
"Or are developers so obsessed with not repeating themselves that making a bit of mocked up HTML to give an idea as what is to come as well as the real thing is just too much to handle?" because that's easy to implement, but it sucks to maintain in large applications. you also have to understand that, a problem can be very small when you're the only programmer, but a nightmare when you're managing 10 devs, with half of them being junior devs.
Overloading async await to also carry staticism/determinism, in addition to asynchrony, feels like a mistake. The elegance of implementation should be a much lower priority than providing users sound abstractions that enable them to solve all aspects of a complicated problem. Glossing over it with async is not the right solution, in my opinion. Enjoyed the video, though.
Commenting my guess as to what this code does before I find out how wrong I am: It hijacks however promises work in JS to ensure the root is rendered before anything else that might depend on it.
Ok so my understanding is we assume that async = dynamic and not-async = static. Then you can opt out of async being treated as dynamic by adding “use cache”. So we just need a way to opt out of not-sync being static. Not async is probably static almost every time, I can’t currently think of a time where it wouldn’t be (but maybe I’m not thinking of something), but it doesn’t literally have to be. For example you could put a math.random thing in there and make a decision based on that, now it’s dynamic. Or I know you can actually read from SQLite without async eg in drizzle, you could also read from a generic in memory store (ie just a js variable) that changes over time. I don’t think that applies here though because I assume it’s only meant to be being rendered serverlessly, where the state of the program is reset after every function call
Components can be forced to be rendered dynamically by calling `await connection()`, which replaces unstable_noStore and "force-dynamic". And there's a catch, async components will become static if every async call inside is cached. Theo should've mentioned this because he ran into this problem in one of his videos called "I built the same app with 5 different stacks". He fetched an array and shuffled it randomly, but since the result of the fetch was cached, the function returned the same result until it was forced to be dynamic.
0:54 i got a question tho: why is the controller abort not wrapped in a try block? it can throw if any of its abort listeners throw. and yes i understand most of it. helps that i've been in varying degrees in the framework business for almost a decade now tho. and that setImmediate trick iirc won't work the same in browsers bc they don't drain the promise queue fully between timer tasks.
>and that setImmediate trick iirc won't work the same in browsers bc they don't drain the promise queue fully between timer tasks. the prerendering happens on the node.js not browser
oh! So you throw two things on the event loop and let the second be a canary for if the first is async.. that should do the trick! got ten minutes on before it clicked though.
So the new Promise() executor function runs synchronously and when it calls setImmediate() it schedules a callback on the tasks queue to be executed as soon as the call stack is empty, the first callback starts the pre-rendering (does this happen in the background, in a microtask?? Not sure) and the second callback terminates the pre-rendering by aborting the controller which signals the pre-render to stop there, then it resolves the promise with the current result (only the static parts) and since both callbacks closed over the same result variable both have access to its latest value which is the static parts of the App pre-rendered.
What about when I have a CMS I want to fetch things from, but still want the results to be cached? Your idea only works for webapps, not static sites. I don't think it's a good idea to implicitly put all the "dynamic" CMS-defined content to end up behind a dynamic suspense.
Every time I learn a new rendering paradigms especially from vercel, I always wish people create videos not just about the web vitals comparison but also the edge requests utilizations for various rendering structures of a website. For small scale indie devs, it gives a lot of breathing space before shelling out money for pricey tiers.
Correct me if I'm wrong, could you not use export const dynamic = 'force-dynamic' to prevent caching? That's much more obvious that randomly calling headers().
"it uses plain JavaScript to know what's cashed or not". I understand that it improved somewhat, but there is still compiler magic in `"use cache"`. A wrapper function would be better IMO.
Good content. Just one question... MDN says setImmediate is deprecated and should not be used. Is that just for the browser, and its ok to use on the server side since its v8 and nodejs?
idk if im wrong but didn't they just go the other way around to indentify cacheable content? i.e before you _explicitly marked content as dynamic_ using functions like headers(), now you need to _explicitly mark content as static-ish_ using "use cache". Might be a stretch but cool way to think about the model tho i guess
Alright, I understood that async makes dynamic component while not async makes static. But then how should completely static pages load their initial data from database and remote servers? This requires async keyword to be able to use awaits (because promises are not cool), which automatically makes it dynamic.
If you load data from database to your page, page is no longer considered as static. Static is if you hardcoded all data once and they don't change ever.
@@LiooRyuuguu that sounds like isr without getStaticProps, but for a page that might not even have it (no dynamic segment). Isnt there proper way to build static pages like before, at build time?
@@vildivent you missed some advancements of past several years. There is thing called isr that allows to make static pages, but rebuild them at runtime when needed
Next.js statically prerenders every component during build by default unless they're explicitly opted-out, either by being wrapped in Suspense or using any Dynamic APIs like searchParams (and there's also the route segment config, but that's being deprecated in favor of dynamicIO). And I think you're misunderstanding ISR, it's just the ability to update a _prerendered_ page, and this prerendering happens initially during build.
Couldn't this be solved by making sure anything that is dynamically loaded as 'use client' and make sure the rest of the page could be statically rendered? That's how i've been understanding use client so it will be a surprise to me, if it doesn't work this way.
@t3dotgg but to solve the problem that you presented in the video where waiting for the slow components was preventing the entire page from rendering wouldn't that have been solved by putting those in components with use client?
Depends on how you fetch the data. If you move the data fetch to an endpoint and the component gets the data via react-query or useEffect, then yes, it's not blocking - only because you wrote it in a non-blocking way. If you fetch the data in the server component and pass it to the client component, the "use client" does not change the static/dynamic nature of the request.
@t3dotgg i see now, thank you. To nextjs credit they did encourage using server actions to get data from client components so as long you did that it likely prevented this issue
When trying to understand the code at the begining, this poped into my mind (I didn't have the async part though ^^) and I was like.... "Hum.... naaaah, must've missed something, this seems....it would give me feelings". It does... 'tis both smart and feels incredibly "shaky" to me, I have so many questions... All JS engines works that way ? What if for some reason big brain me wants to use callbacks ? This seems like a dangerous toy to bring to school ! isn't it ? Moar questions, maybe...
I'm still not sure how this works - wish there was more of a technical explanation code-wise. nodejs setImmediate doesn't do anything funny in terms of task queue, it doesn't put anything in front - it work essentially the same way setTimeout(cb, 0) does, it just puts the callback on a macro task queue. So if prerender is doing a bunch of async work with promises - then most of it is gonna go to a micro task queue(cause that's what .then or await do) which will be drained before next macro task. So the second setImmediate will fire after most of prererender async work. It would work if you replaced setImmediate with nextTick or queueMicrotask - I understand that obviously it does work as is on the screenshot but I don't understand why. Either they're using some non-standard setImmediate or doing something funny with async components which doesn't put them on a microtask queue(no idea how would you do that tbh) Also is there an await missing in front of new Promise? Am I crazy cause why nobody else is asking about it?
Function color is a terrible indicator of this. It indicates long-running work, sure, but not necessarily "non-static server-side work" as required. It does not indicate whether a function is deterministic. A function is deterministic if all its dependencies are deterministic. It's that simple, there's no alternative. Either the dependency graph must be tracked at the language level, or an explicit label must be set by the developer. But forcing developers to use function color as an indicator is going to result in terribly written code. Please do not push this, and instead reconsider your perspective! I don't suggest conflating two clearly distinct concepts because of surface-level convenience.
I’m a bit of a hater of the JS world but I think this way of doing things makes perfect sense. You say async functions indicates long running work. No, they indicate asynchronous work. Why do you use async for? For I/O. What is a property of I/O? It’s indeterministic. Ok, so we can assume async functions are not deterministic. What about not async ones? Well, those will be deterministic most of the time. How can you have a function that does no I/O but is not deterministic? By doing stuff like reading global variables and so on. Which seems to be uncommon in JS. So most of the time they will be deterministic. If you allow to use a directive like “use dynamic” in non async functions in case you are doing weird stuff, it should be fine. This combination of async and sync functions with “use dynamic” allows to determine whether *user* code is dynamic or not. For dependencies, it’s enough most of the time but not always. If you use a dependency that renders dynamic content, it’s gonna be async most of the time, forcing your function to be async. But some dependency could render dynamic content using sync functions.
To be fair I see a lot of potential problems with this, but most of the time it will work fine. There’s some edge cases that can bite you tho. Anyways that’s frameworks for you anyway. If you want convenient magic it will eventually hurt you. If you want control over your code base and behavior of your project, and don’t want surprises, you’re better off using no framework. I don’t think what we saw in the video can be implemented without any gotchas. If they put this behind a feature flag is fine.
I think use cache basically allow for the opt in to be “sync”. But yes there will be some requests that get a reduced response. if the App accesses headers at all doesn’t it just opt out of any pre-rendering. Which I guess was the previous behaviour. But then you can’t use a specific header as a cache key you end up making the whole thing dynamic - I am thinking maybe adapting the shell depending on the role in the users claims. The solution there might be to see if you can add an async function before every request on your edge and sending the next app your variables you want to cache on. That’s a bit custom tho and no idea if that’s possible.
The headers was fine it just means there is a request header so it’s dynamic as it’s based on a user interaction. No request header no user interaction. If you need a request header and it to be cached use a dynamic function and add the cache header yourself
@@TheYinyangman ahh yes I think I misunderstood. In next, is this pre-rendering code part of the build step so the outputted html is then set to a CDN for actual request time ? I kind of thought of it still producing a serverless function (that can be deployed to the edge) and the function has html ready rather than React style rendering i.e. it is pre-rendered.
2024.. I'm getting freakin old I guess.. (36 now) more than a decade ago I've already specially tailored slow components to asyncronously inject state into context.. using a custom self-made implementation of promises (essentially a proper callback wrapper), before we had promises.. in IE 5.. so.. having figured what this code does, (literally exploiting a reproducible race condition), as I know event loop control pretty damn well.. where is that job you were talking about? I legit went into sys administration and backend dev because I got sick and tired of bad leadership in full-stack environments..
before watching this, I'm guessing it has something to do with detecting async code or doing some kind of partial render until the first async or suspense thing
pretty much spot on. train of thought included JS event loop, what AbortControllers are for, what prerendering is, and the concept of partial prerendering came to mind let me know if there are any jobs I can get while still in school (done with everything except for required CS upper divisions, since I came in with so many AP and dual credit courses)
The message is that we don't have to block entire page cos of one slow component. And if there's no waiting in layout we can store it in cash as static data.
@@vildivent I get it.. lol. I'm just pointing out, it doesn't make the slowest thing any faster; so in the end, you're not actually making anything faster. I guess: unless you need to click on something that has already rendered- then you're not waiting for that 'slowest" thing and are already moving on to the next page/link, etc. But most of the time, the "static" info is just text and aesthetic stuff. IDK.. It's not much of a change. Getting everyone fast computers and fiber internet is what really makes things faster. These little incremental/remedial tweaks are... nominal.. marginal... superficial; it's not really doing anything- just the appearance of doing something... cosmetic.. aesthetic. The page still can't really function until it loads. lol.. Just because you "see" a few things earlier, doesn't really help anything. IDK.. I'm just being a negative nancy, apparently
JavaScript is like women. I love 😍 JavaScript with all my heart but I will never understand how or why JavaScript works. I just know it does and without it I would not be here
As a C developer I can confidently say I have no fracking idea what a promise is, also what's a try-catch statement? Anyways, still love watching these videos and the technology behind web dev, even though I will probably never touch Next.js in my life.
@@kassios It's some sort of morbid curiosity or something, but hey at least you guys don't have to ever have to deal with self modifying code, bit-banging custom quad-state serial interfaces, or eye diagrams!
@@linuxguy1199 that's why we rely on you guys to actually make things move, while we safely play ontop of a compiler to create the most complicated button to present to management. 😃
good tech tho fo sho. there's more symmetries to be exploited for compiler efficiency. and there are more symmetries to uncover that expand the capabilities of compilers.
actually informative content wow
bruh you haven't even finished watching the video yet
Finnaly not a clickbait titile 😭
This is beautifully simple and brilliant! I love elegant solutions that rely on basic language features over magical logic.
I can't believe it, but I actually understood the code from 0:50 without explanation from the video. Needed to sneak a peek at core-js's implementation of setImmediate tho.
My basic understanding was:
prerender is a function that can take some time to run or might not, so using setImmediate we can stop "rendering" and get intermediate result from synchronous part of rendering process.
this is my interpretation: prerender has async inside, setImmediate puts a callback before any async tasks so after prerender completes with every sync task and moves to async tasks the setImmediate callback is executed which aborts the async tasks via the abort controller and resolves all the non-async prerendered react stuff
Was literally playing with suspense / loading / dynamicIO yesterday, it's like you're reading my mind. Great stuff!
Oh I was so confused with `params` throwing errors they should be awaited. The explanation is great, thanks Theo!
17:28 'And I can even delete this suspense.' It seems that from this point, all SlowComponents are delayed by 5 seconds.
Yes, that part looked like a step back, but things start to make sense again a few seconds later, when he moves all the code into that new async Content component.
@federicomedinauy i didn't notice that. IIRC it remains like that and the timeout remains same for all slow components.
this is a really good video, thanks for explaining that code well
seeing destructuring of new promise into prelude and postponed without awaiting it leave so many questions
Enticed by the prelude, but explanation remains postponed.
thank you Theo this is the best Next.js video i've seen all year
So, i paused it where you showed it. This is what I found.
This code shows how to manage server-side rendering with React using prerender and an AbortController. The prerender function renders a React component () on the server, and the AbortController allows you to cancel the rendering if it takes too long. A promise is used to handle the process, ensuring the result is either successfully completed or an error is caught if something goes wrong.
Two setImmediate calls are used to control the flow. The first tries to render the component and handles any errors. The second cancels the render using the AbortController and resolves the promise with the result, even if the process was stopped early. This setup ensures rendering doesn't hang and that server resources are used efficiently. It's helpful for managing timeouts and handling errors in server-side rendering.
Lets see how close I am.
very nice explanation, love those advanced video
This is literally what I love most about javascript lol ❤
Classic Excalidraw Theo!! YES
I missed these excalidraw videos! MOOOOOOOOORE please!
This is dope and feels so obvious once you see it
Amazing. I started to like React this year!
The moment I saw those setImmediate and controller abort, I assumed there was some funny prevent async loops to get kicked in shenanigans. This is such a clever way of using JavaScript's (or in this case, Node.js's Event Loop)
2:05 you just love putting yourself on a pedestal, even if not at the top 😂
Videos like these make all the clickbait stuff tolerable 😅. Thank you!
18:25 even though if you remove dynamicIO flag, isn't it will work the same way? By separating Contents component?
No they swap out the function.
They use renderToStream for non dynamic io
Without dynamicIO (and no PPR enabled) the pages are either completely static or dynamic. PPR in Next.js 14 enabled the combination of static and dynamic parts on the same page, and dynamicIO builds on this concept while also adding "use cache" to the mix.
What I'm surprised about it how prerender doesn't fail when the promises it listens to are not responded to. I'm guessing that's what the AbortController does (rejecting all invoked promises) but it's impressive that prerender can figure out how to handle what to do about components which don't resolve (maybe it just returns blank for those, idk)
10:45 Question about the TopNav (which has the "use client"). I follow when you say that's static and should be rendered ASAP, how does the client side js work out? I mean what if we are using client data (AV info, date time, etc)? Where does that fall into the event loop and stack or whatever?
This is extremely elegant
So, simple question. Why do you just do a static HTML page with all the placeholders and state by default and then bring in dynamic content in every case? In essence, force the developers to make as much of the site static as possible. Take the pokemon page. Make it static with the default greyed out pokemon choices that boom, get replaced with non-greyed out new choices when they are ready (or when the pick new random button is chosen).
Also, why do components not require static placeholder content? I am thinking back to the old days of UI builders, when you brought in a component, it'd be run "in design time" and the content was often just a placeholder that you could still see how to fit into the app, but with faked or blank data.
In that case, just have the framework be able to spit out a static rendered site to preview the design as well as the placeholder static HTML until all the scripts run and actual content is ready with just a flag? Or are developers so obsessed with not repeating themselves that making a bit of mocked up HTML to give an idea as what is to come as well as the real thing is just too much to handle?
ETA: I don't think the above model is inconsistent with server side rendering in general. After all, nothing says that if you have a page with some components, you have then have to construct all the HTML on the client. The components may happily serve back the HTML to be placed inside the page, I would imagine.
womp womp
Yes this. The video doesn't quite explain why anyone would allow a slow component that takes more than 2 seconds to block everything server side and not just turn that into a client side component that fetches data from the client. Maybe there is a reason but this didn't explain why this problem exists to begin with.
"Or are developers so obsessed with not repeating themselves that making a bit of mocked up HTML to give an idea as what is to come as well as the real thing is just too much to handle?"
because that's easy to implement, but it sucks to maintain in large applications. you also have to understand that, a problem can be very small when you're the only programmer, but a nightmare when you're managing 10 devs, with half of them being junior devs.
You've just described ssr :)
About placeholders: why do you mention some "old days"? They're everywhere. It's just getting easier to use them.
Overloading async await to also carry staticism/determinism, in addition to asynchrony, feels like a mistake. The elegance of implementation should be a much lower priority than providing users sound abstractions that enable them to solve all aspects of a complicated problem. Glossing over it with async is not the right solution, in my opinion.
Enjoyed the video, though.
Very well explained! 👏
Wow. Just wow!
swear to go I understood it immediately.
give me a job Theo .\/.
Commenting my guess as to what this code does before I find out how wrong I am:
It hijacks however promises work in JS to ensure the root is rendered before anything else that might depend on it.
Ok so my understanding is we assume that async = dynamic and not-async = static. Then you can opt out of async being treated as dynamic by adding “use cache”. So we just need a way to opt out of not-sync being static. Not async is probably static almost every time, I can’t currently think of a time where it wouldn’t be (but maybe I’m not thinking of something), but it doesn’t literally have to be. For example you could put a math.random thing in there and make a decision based on that, now it’s dynamic.
Or I know you can actually read from SQLite without async eg in drizzle, you could also read from a generic in memory store (ie just a js variable) that changes over time. I don’t think that applies here though because I assume it’s only meant to be being rendered serverlessly, where the state of the program is reset after every function call
Components can be forced to be rendered dynamically by calling `await connection()`, which replaces unstable_noStore and "force-dynamic". And there's a catch, async components will become static if every async call inside is cached. Theo should've mentioned this because he ran into this problem in one of his videos called "I built the same app with 5 different stacks". He fetched an array and shuffled it randomly, but since the result of the fetch was cached, the function returned the same result until it was forced to be dynamic.
0:54 i got a question tho: why is the controller abort not wrapped in a try block? it can throw if any of its abort listeners throw.
and yes i understand most of it. helps that i've been in varying degrees in the framework business for almost a decade now tho. and that setImmediate trick iirc won't work the same in browsers bc they don't drain the promise queue fully between timer tasks.
>and that setImmediate trick iirc won't work the same in browsers bc they don't drain the promise queue fully between timer tasks.
the prerendering happens on the node.js not browser
setImmediate is noted as deprecated on the MDN docs. Does this not matter because Node says it’s supporting it?
Correct. Less useful feature in the browser
Thanks for sharing
Beautiful video. But how was it working before use cache or before PPR? Also pager router did this how?
We just had something similar to Suspense in Inertia 2.0
what do you use for these boards? Like what you pulled up at 2:10
excalidraw
He is hosting exaclidraw himself,
If you want there is excalidraw plugin in obsidian or logseq to use offline
This video makes me want to refactor every prod app during the holiday, please stop uploading for a few weeks pls thx
oh! So you throw two things on the event loop and let the second be a canary for if the first is async.. that should do the trick!
got ten minutes on before it clicked though.
So the new Promise() executor function runs synchronously and when it calls setImmediate() it schedules a callback on the tasks queue to be executed as soon as the call stack is empty, the first callback starts the pre-rendering (does this happen in the background, in a microtask?? Not sure) and the second callback terminates the pre-rendering by aborting the controller which signals the pre-render to stop there, then it resolves the promise with the current result (only the static parts) and since both callbacks closed over the same result variable both have access to its latest value which is the static parts of the App pre-rendered.
hard not to love nextjs
What about when I have a CMS I want to fetch things from, but still want the results to be cached?
Your idea only works for webapps, not static sites. I don't think it's a good idea to implicitly put all the "dynamic" CMS-defined content to end up behind a dynamic suspense.
I only use JS to draw some boxes. Not because I want to but because browsers force me to. (Actually TS).
Every time I learn a new rendering paradigms especially from vercel, I always wish people create videos not just about the web vitals comparison but also the edge requests utilizations for various rendering structures of a website. For small scale indie devs, it gives a lot of breathing space before shelling out money for pricey tiers.
thank you!
Correct me if I'm wrong, could you not use export const dynamic = 'force-dynamic' to prevent caching? That's much more obvious that randomly calling headers().
"it uses plain JavaScript to know what's cashed or not".
I understand that it improved somewhat, but there is still compiler magic in `"use cache"`. A wrapper function would be better IMO.
Good content. Just one question... MDN says setImmediate is deprecated and should not be used. Is that just for the browser, and its ok to use on the server side since its v8 and nodejs?
thanks for the video
idk if im wrong but didn't they just go the other way around to indentify cacheable content?
i.e before you _explicitly marked content as dynamic_ using functions like headers(),
now you need to _explicitly mark content as static-ish_ using "use cache".
Might be a stretch but cool way to think about the model tho i guess
Alright, I understood that async makes dynamic component while not async makes static. But then how should completely static pages load their initial data from database and remote servers? This requires async keyword to be able to use awaits (because promises are not cool), which automatically makes it dynamic.
can‘t you use the „use cache“ flag so it gets cached and therefore becomes static on second render?
If you load data from database to your page, page is no longer considered as static. Static is if you hardcoded all data once and they don't change ever.
@@LiooRyuuguu that sounds like isr without getStaticProps, but for a page that might not even have it (no dynamic segment). Isnt there proper way to build static pages like before, at build time?
@@vildivent you missed some advancements of past several years. There is thing called isr that allows to make static pages, but rebuild them at runtime when needed
Next.js statically prerenders every component during build by default unless they're explicitly opted-out, either by being wrapped in Suspense or using any Dynamic APIs like searchParams (and there's also the route segment config, but that's being deprecated in favor of dynamicIO). And I think you're misunderstanding ISR, it's just the ability to update a _prerendered_ page, and this prerendering happens initially during build.
Couldn't this be solved by making sure anything that is dynamically loaded as 'use client' and make sure the rest of the page could be statically rendered? That's how i've been understanding use client so it will be a surprise to me, if it doesn't work this way.
You don't understand "use client". Files marked "use client" still run on the server.
@t3dotgg but to solve the problem that you presented in the video where waiting for the slow components was preventing the entire page from rendering wouldn't that have been solved by putting those in components with use client?
Depends on how you fetch the data. If you move the data fetch to an endpoint and the component gets the data via react-query or useEffect, then yes, it's not blocking - only because you wrote it in a non-blocking way.
If you fetch the data in the server component and pass it to the client component, the "use client" does not change the static/dynamic nature of the request.
@t3dotgg i see now, thank you. To nextjs credit they did encourage using server actions to get data from client components so as long you did that it likely prevented this issue
@ they never encouraged servers actions for data fetching. Common misconception I’ve tried really hard to clear up.
do more videos like this 🙂
When trying to understand the code at the begining, this poped into my mind (I didn't have the async part though ^^) and I was like.... "Hum.... naaaah, must've missed something, this seems....it would give me feelings".
It does... 'tis both smart and feels incredibly "shaky" to me, I have so many questions...
All JS engines works that way ? What if for some reason big brain me wants to use callbacks ? This seems like a dangerous toy to bring to school ! isn't it ? Moar questions, maybe...
whats the code look like in astro?
I'm still not sure how this works - wish there was more of a technical explanation code-wise.
nodejs setImmediate doesn't do anything funny in terms of task queue, it doesn't put anything in front - it work essentially the same way setTimeout(cb, 0) does, it just puts the callback on a macro task queue. So if prerender is doing a bunch of async work with promises - then most of it is gonna go to a micro task queue(cause that's what .then or await do) which will be drained before next macro task.
So the second setImmediate will fire after most of prererender async work.
It would work if you replaced setImmediate with nextTick or queueMicrotask - I understand that obviously it does work as is on the screenshot but I don't understand why. Either they're using some non-standard setImmediate or doing something funny with async components which doesn't put them on a microtask queue(no idea how would you do that tbh)
Also is there an await missing in front of new Promise? Am I crazy cause why nobody else is asking about it?
Could you give some short introduction, in the beginning, what this video is actually about, please.
"Election meltdown watch party." 😂😂😂
A promise can be destructiveed?
That code doesnt look complexed, I seen worst code just to manage a bit of state
DynamicIO should be called DynamicAsync. It's not about IO, it's about Async.
Naming is hard...
Stupid Question alert: what are you using to draw? Is this a free tool?
Excalidraw, yes its free and opensource
Function color is a terrible indicator of this. It indicates long-running work, sure, but not necessarily "non-static server-side work" as required. It does not indicate whether a function is deterministic. A function is deterministic if all its dependencies are deterministic. It's that simple, there's no alternative. Either the dependency graph must be tracked at the language level, or an explicit label must be set by the developer. But forcing developers to use function color as an indicator is going to result in terribly written code. Please do not push this, and instead reconsider your perspective! I don't suggest conflating two clearly distinct concepts because of surface-level convenience.
I’m a bit of a hater of the JS world but I think this way of doing things makes perfect sense.
You say async functions indicates long running work.
No, they indicate asynchronous work. Why do you use async for? For I/O.
What is a property of I/O? It’s indeterministic.
Ok, so we can assume async functions are not deterministic.
What about not async ones?
Well, those will be deterministic most of the time.
How can you have a function that does no I/O but is not deterministic?
By doing stuff like reading global variables and so on. Which seems to be uncommon in JS.
So most of the time they will be deterministic.
If you allow to use a directive like “use dynamic” in non async functions in case you are doing weird stuff, it should be fine.
This combination of async and sync functions with “use dynamic” allows to determine whether *user* code is dynamic or not.
For dependencies, it’s enough most of the time but not always.
If you use a dependency that renders dynamic content, it’s gonna be async most of the time, forcing your function to be async.
But some dependency could render dynamic content using sync functions.
To be fair I see a lot of potential problems with this, but most of the time it will work fine. There’s some edge cases that can bite you tho.
Anyways that’s frameworks for you anyway.
If you want convenient magic it will eventually hurt you.
If you want control over your code base and behavior of your project, and don’t want surprises, you’re better off using no framework.
I don’t think what we saw in the video can be implemented without any gotchas.
If they put this behind a feature flag is fine.
I think use cache basically allow for the opt in to be “sync”. But yes there will be some requests that get a reduced response.
if the App accesses headers at all doesn’t it just opt out of any pre-rendering. Which I guess was the previous behaviour. But then you can’t use a specific header as a cache key you end up making the whole thing dynamic - I am thinking maybe adapting the shell depending on the role in the users claims. The solution there might be to see if you can add an async function before every request on your edge and sending the next app your variables you want to cache on. That’s a bit custom tho and no idea if that’s possible.
The headers was fine it just means there is a request header so it’s dynamic as it’s based on a user interaction. No request header no user interaction. If you need a request header and it to be cached use a dynamic function and add the cache header yourself
@@TheYinyangman ahh yes I think I misunderstood. In next, is this pre-rendering code part of the build step so the outputted html is then set to a CDN for actual request time ? I kind of thought of it still producing a serverless function (that can be deployed to the edge) and the function has html ready rather than React style rendering i.e. it is pre-rendered.
2024.. I'm getting freakin old I guess.. (36 now)
more than a decade ago I've already specially tailored slow components to asyncronously inject state into context.. using a custom self-made implementation of promises (essentially a proper callback wrapper), before we had promises.. in IE 5..
so.. having figured what this code does, (literally exploiting a reproducible race condition), as I know event loop control pretty damn well..
where is that job you were talking about?
I legit went into sys administration and backend dev because I got sick and tired of bad leadership in full-stack environments..
But.. SetImmediate is deprecated right?
AFAIK, its a Node-only thing that used to also be available in IE. So it's useful in Node, but deprecated in browser
22:14 - you are wrong, it is not "javascript itself". setImmediate and even the event loop doesn't present in js specification.
So, does it mean that we don't need Astro anymore?
before watching this, I'm guessing it has something to do with detecting async code or doing some kind of partial render until the first async or suspense thing
pretty much spot on. train of thought included JS event loop, what AbortControllers are for, what prerendering is, and the concept of partial prerendering came to mind
let me know if there are any jobs I can get while still in school (done with everything except for required CS upper divisions, since I came in with so many AP and dual credit courses)
Anyone here thinks too, that Next.js is too complicated spaghetti ?
nice video
So we still have to wait for the slowest thing to load anyway... cool
The message is that we don't have to block entire page cos of one slow component. And if there's no waiting in layout we can store it in cash as static data.
@@vildivent I get it.. lol. I'm just pointing out, it doesn't make the slowest thing any faster; so in the end, you're not actually making anything faster. I guess: unless you need to click on something that has already rendered- then you're not waiting for that 'slowest" thing and are already moving on to the next page/link, etc. But most of the time, the "static" info is just text and aesthetic stuff. IDK.. It's not much of a change. Getting everyone fast computers and fiber internet is what really makes things faster. These little incremental/remedial tweaks are... nominal.. marginal... superficial; it's not really doing anything- just the appearance of doing something... cosmetic.. aesthetic. The page still can't really function until it loads. lol.. Just because you "see" a few things earlier, doesn't really help anything. IDK.. I'm just being a negative nancy, apparently
Its honestly really not that complex considering how framework code gets. Like, thats basic tier stuff in that world
I don't like magic
🍋
delicious
JavaScript is like women. I love 😍 JavaScript with all my heart but I will never understand how or why JavaScript works. I just know it does and without it I would not be here
As a C developer I can confidently say I have no fracking idea what a promise is, also what's a try-catch statement? Anyways, still love watching these videos and the technology behind web dev, even though I will probably never touch Next.js in my life.
Look about an event loop. The code is gonna wait for that to happen to then run the thenable or the next line. Or throw an exception
Why do that to yourself my friend? Why share our frontend pain and tribulations…
@@wil-fri What's an event?
@@kassios It's some sort of morbid curiosity or something, but hey at least you guys don't have to ever have to deal with self modifying code, bit-banging custom quad-state serial interfaces, or eye diagrams!
@@linuxguy1199 that's why we rely on you guys to actually make things move, while we safely play ontop of a compiler to create the most complicated button to present to management. 😃
2000 IQ
uh, duh
good tech tho fo sho. there's more symmetries to be exploited for compiler efficiency. and there are more symmetries to uncover that expand the capabilities of compilers.
nextjs was a mistake
weird as hell
4:28 - The web has never worked this way. As usual, once you take away the mess of abstractions, Theo fundamentally misunderstands the web platform.
First comment, YAY!
all these videos are obsessed with TTFB. no one cares about building anything anymore
2 views in 1 min, bro fell off
Xqc ahh comment
Press refresh.. It doesn't load life data.
first
Meh, I was asked about a similar piece of code back in 2020 during one of my interviews. Of course, it had a much easier use case, but nevertheless
Thanks!🙏