For instance, you can write
x = [1..]
head x -- this is 1
head (tail x) -- this is 2
However, having to evaluate (or "force") thunks repeatedly in tight loops can obviously degrade performance: printing a Haskell list like [1..10] requires you to evaluate the first element, print it, then force the next thunk to print the 2, and so on. Comparing this to the equivalent in basically any other (strict) language, we see that a lot of indirection is removed, because the endless wrapping/unwrapping is replaced with a simple linked list of 10 elements. (I'm sure GHC is smart enough to optimize the thunking away in this toy case sufficiently.)
One might also say that a thunk is a "promise" (I'm not familiar with the JS concept of the same name, which I think is related to async things instead) to give you a certain value by calculating it only when you need it (if you do at all: a thunk stays unevaluated when you're done running the program, it's GC'd away).