Embracing the Combined Power of Worker Threads and Server Actions in Next.js
Worker Threads in Node.js
A worker thread is a separate thread of execution that runs concurrently with the main thread in a program. It operates independently and can perform tasks in parallel, allowing the application to handle multiple operations simultaneously. The primary purpose of worker threads is to offload computationally intensive or time-consuming tasks from the main thread, preventing it from being blocked and ensuring that the application remains responsive.
In Node.js, worker threads are a feature introduced to enable parallelism in JavaScript applications. They provide a mechanism for running JavaScript code in separate threads, distinct from the main event loop. This is particularly useful in scenarios where certain operations, such as heavy computations, might otherwise cause the main thread to become unresponsive.
Worker threads operate in their isolated context, meaning they have their own set of variables and do not share the same global scope as the main thread. This isolation helps avoid conflicts and issues related to shared state between threads.
Typically, worker threads are employed when an application needs to perform CPU-bound tasks, such as data processing, image manipulation, or complex algorithmic computations. By distributing these tasks across multiple threads, the overall performance and efficiency of the application can be significantly improved, especially on multi-core systems where each thread can run on a separate core.
Benefits of a Worker Thread
Using worker threads in Node.js can bring several advantages to your application, particularly in scenarios where parallel processing or offloading heavy computations is beneficial. Here's a summary of why you should consider using worker threads in Node.js:
What is Server Action?
From the official documentation of Next.js, we can say that Server Actions are asynchronous functions that are executed on the server. They can be used in Server and Client Components to handle form submissions and data mutations in Next.js applications. Server actions are super handy and easy to use.
Here are some reasons why you'll love Server Actions:
Let’s stop theories and dive into implementation
Let's initiate a Next.js project with
npx create-next-app@latest
Now, I have cleared the main page like this,
// app/page.tsx
import Buttons from "./buttons";
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24">
<Buttons />
</main>
);
}
// app/buttons.tsx
"use client";
import {
myHeavyTaskInWorkerThread,
myHeavyTaskInMainThread,
myLightTaskInMainThread,
} from "./action";
const Buttons = () => (
<div className="flex gap-3">
<button
className="bg-gray-700 p-3 rounded-lg hover:bg-black hover:border-white border"
onClick={() => myHeavyTaskInWorkerThread()}
>
Call Heavy Task In Worker Thread
</button>
<button
className="bg-gray-700 p-3 rounded-lg hover:bg-black hover:border-white border"
onClick={() => myHeavyTaskInMainThread()}
>
Call Heavy Task In Main Thread
</button>
<button
className="bg-gray-700 p-3 rounded-lg hover:bg-black hover:border-white border"
onClick={() => myLightTaskInMainThread()}
>
Call Light Task In Main Thread
</button>
</div>
);
export default Buttons;
Now my UI looks like this,
Now Let's explain the code a bit,
领英推荐
I have made a button component where I have three buttons. Each one of them will call a server action.
export async function myHeavyTaskInMainThread() {
console.log("Called myHeavyTaskInMainThread");
let sum = 0;
for (let i = 1; i <= 10000000000; i++) {
sum += i;
}
console.log("Result from myHeavyTaskInMainThread:", sum, "\n");
return sum;
}
This is a simple task that will take some time to execute and will eventually block the main thread. We will see it in action soon.
2. myLightTaskInMainThread
export async function myLightTaskInMainThread() {
console.log("Called myLightTaskInMainThread");
let sum = 0;
for (let i = 1; i <= 10; i++) {
sum += i;
}
console.info("Result from myLightTaskInMainThread:", sum, "\n");
return sum;
}
This is a similar task that won't take much time to execute and will not block the main thread.
3. myHeavyTaskInWorkerThread
export async function myHeavyTaskInWorkerThread() {
console.log("Called myHeavyTaskInWorkerThread");
if (isMainThread) {
const worker = new Worker("./app/worker.js", {
workerData: {
task: "performSum",
targetValue: 10000000000,
},
});
worker.on("message", (result) => {
console.log("Result from myHeavyTaskInWorkerThread:", result, "\n");
});
}
}
This is also a server action but unlike others, it will create a worker and execute a script that is equivalent to the heavy task on the main thread function.
// app/worker.js
const { parentPort } = require("worker_threads");
function aHeavyTask() {
let sum = 0;
for (let i = 1; i <= 10000000000; i++) {
sum += i;
}
return sum;
}
const result = aHeavyTask();
parentPort.postMessage(result);
Now, When I click on the button with the title "Call Heavy Task In Main Thread", it blocks the main thread for some time. During that time no matter how many times I click on the button with the title "Call light Task In Main Thread", it won't do anything which means it is now blocked and queued. As soon as the task is finished it will execute the tasks from the queue. but the same task if I call through the button with the title "Call Heavy Task In Worker Thread", it will not block the main thread and will keep executing the next commands. let's watch it in action.
So in the video, you can see when I clicked on the heavy task on the main thread it blocked the other code executions and later once the heavy task was finished it executed the other task. It can be a huge issue if you have any heavy CPU-intensive task and it blocks other user's requests. But when we used the worker thread it kept working on the task but also parallelly continued executing other tasks.
Conclusion
In conclusion, harnessing Worker Threads and Server Actions empowers developers to craft more responsive, efficient, and scalable Next.js applications.
Key takeaways:
Ready to embrace the future of Next.js development?
Unleash the full potential of Next.js and create web experiences that surpass expectations!
Software Developer | Robotics & ML Enthusiast | Engineering Student
11 个月This doesn't seem to work. The worker file is not separated during the build and gets compiled in the same file. The worker cannot find the file.