What is an async thunk?
Explanation
Let's be honest—"thunk" is a weird word. But the concept behind it is actually quite simple, and you've probably used it without knowing the name.
At its core, a thunk is just a fancy name for a function that delays a calculation.
Part 1: The Regular (Synchronous) Thunk
Imagine you have a math problem: 1 + 2.
If you write this in JavaScript, the computer solves it immediately:
let x = 1 + 2;
// x becomes 3 right now.But what if you don't want to solve it yet? What if you want to save that calculation for later? You wrap it in a function:
let foo = () => 1 + 2;That function foo? That's a thunk.
It's holding the calculation 1 + 2 in suspended animation. The math hasn't happened yet. It only happens when you finally call foo().
So, a synchronous thunk is just a function with zero arguments that returns a value.
Part 2: The Async Thunk
Now, let's take that idea and apply it to something that takes time, like fetching data from an API.
An async thunk is still just a function that delays work. But because the work is asynchronous (like waiting for a server), it can't just return the value immediately when you call it.
Instead, it usually does one of two things:
- It accepts a callback function ("Call me when you're done").
- It returns a Promise ("Here's a receipt, I'll have your data soon").
Here is what that looks like in code:
// The setup (defining the thunk)
const fetchUserThunk = (userId) => {
// We return a function (the thunk) that does the actual work
return (callback) => {
console.log("Starting to fetch...");
setTimeout(() => {
callback(`User data for ${userId}`);
}, 1000);
};
};
// The usage
const getAbhishek = fetchUserThunk(123); // No network request yet!
// ... later on ...
getAbhishek((data) => {
console.log("Got it:", data);
});Why do we even care?
You might be thinking, "Why not just call the API directly? Why wrap it in this extra function?"
The magic happens when you want to separate defining the work from doing the work.
This is why Redux Thunk is so popular. It lets you tell Redux: "Hey, I have this complex sequence of fetching user data, checking their permissions, and then updating the UI." You wrap all that logic into a single async thunk.
Your UI components don't need to know about API calls, timers, or complex logic. They just say:
dispatch(fetchUserThunk(123))
The component doesn't care how it happens or when it finishes. It just hands off the "thunk" to Redux, and Redux executes it.
Modern Example
In modern JavaScript, we mostly use Promises/Async-Await, but the pattern is the same.
// This function returns a thunk
const fetchUser = (id) => async () => {
const response = await fetch(`/api/users/${id}`);
return response.json();
};
// Create the thunk (delayed work)
const getUserAction = fetchUser(42);
// Execute it whenever you want
getUserAction().then(data => console.log(data));