I think a thunk?

So I was reading an article about Demystifying Modern Frontend Jargon and ran into this term:

  1. Thunk
    In computer programming, a thunk is just another word for a function. But this is a special name for a function that’s returned by another function and it takes no arguments.
    Thunks are used to delay or defer a computation until its result is needed, or to insert operations at the beginning or end of the other computations. Then when that thunk is invoked, it performs a potentially expensive computation and/or causes some side effect to occur.

with the following example

function add(x, y) {
    // this is a thunk because it defers work for later
    return function() { // it can be named, or anonymous
        return x + y; 
    };
}

const thunk = add(1,2); // we can store the returned function to a variable
// OR use it directly like ➡️ add(1,2)()

// we can pass the thunk to different places
checkIfEven(thunk);

// we can use it in other calculations
const twice = thunk() * 2;

I think I can understand how or what a thunk is, but what I don’t understand is what use-case would one want to use this for? To me it seems like a glorified closure function.

I was wondering if anyone has every used this, and if they did why?

The most common use case for thunk I’ve seen, is for a pretty popular (13k stars on GH) Redux library called
Redux Thunk.

In the Readme they have some links about the reasoning behind using a thunk.

Hope this can help :+1:

3 Likes

In this case it is a closure function. The term “thunk” is more typically used to describe an activation record (aka a “stack frame” in most languages) that represents some sort of deferred or incomplete calculation. In other words, it’s more usually used as a description of internal compiler and/or interpreter state rather than describing any high level language construct, where “closure” or just plain “function” is used instead.

That’s not to say you’ll never see the term used in a high-level language; Redux Thunk is one example mentioned above. But in this case, it’s Redux acting as a delayed evaluator, so the term “thunk” still keeps the same connotations I mentioned.

1 Like

Use case is that you want set up a function to run, but you want it to resolve to a value at some future point, not now. Like your add function: say you have those values you want to add together now in your program, but you only want it to resolve later in the program, when you don’t have the values. Redux Thunk is a good example as well – you have a set of actions that need to be dispatched in order, and you need to define them together, probably with a pause inbetween, but Redux is synchronous and only allows one dispatch at a time, so there needs to be a mechanism where you can delay the dispatch of certain of those actions until something else resolves.

As a concrete example in the project sitting in front of me:

I want to mock an entire API for testing and development. I could just write out some mocks, but I’d rather be able to generate arbitrary amounts of responses for all the endpoints. This means, for development, I can switch to the mock API for developing the app quickly with as much or as little data as I want. I’m using a library called Faker to generate data, which provides a load of functions that return random stuff. I also need quite specific stuff, like real-looking AWS IDs, which are partially random but have fixed parts to them. There are also values that I don’t need randomised, or don’t care about, but because the interface of the mock API has to exactly match the real one, I can’t miss them out.

So I have a function called make (and makeList and makeRecord which just run the the make function n times, generating that many list items or object entries). This is a factory for generating data. It accepts an object that looks exactly the same as one API response object, except that every value is a function. The ones that are directly generated by Faker can just be the Faker functions. But the other values are all thunks, because to simplify the generation, make just walks the entire object, expecting a function for every field, and runs it. I know what each function should resolve to, but I don’t want them to run immediately, only when run within that make function

This is closure that acts like a thunk (or thunk in a form of a closure, whatever sounds better to you):

function add(x, y) {
    return function() {
        return x + y; 
    };
}

This is thunk:

const thunk = () => 1 + 2;

And no-one really uses this tern anymore apart from Redux Thunk

It’s a specific term relating to a very specific software pattern, so that’s not really true, it’s just that thunks are not used very often (and have stupid name) (and yeah, the add example isn’t a thunk)

The add example takes zero args, so actually I’d say it’s quite thunk-ish. Just not a very useful pattern without any context to it.

1 Like

I like this idea of thinking of a thunk as a delayed evaluator.

Thank you to all of you for the great answers :smile:. At this point I understand the concept as essentially a closure that I can run later. The concept has gained popularity due to the after mentioned redux thunk lib.

So it not really some fancy mumbo jumbo, but rather just an approach to using/leveraging closures (which could be mumbo jumbo to some people, but its more or less the same as a thunk)

And yes I agree, it has a dumb name :laughing:

I like the name “thunk”. It’s cute. People have made up etymologies for it like “the compiler already thunk of it”, but I personally think whomever coined the term just meant it to mean “thingie”.

Check out a compiler technique called “lazy evaluation” sometime: it uses thunks all over the place.