# The meaning and usage of Thunk function ## One, the evaluation strategy of parameters

The Thunk function was born as early as the 1960s.

At that time, programming languages ​​had just started and computer scientists were still studying how to write compilers better. One point of contention is the “evaluation strategy” , that is, when the parameters of a function should be evaluated.

``````
var x = 1;

function f(m){
return m * 2;
}

f(x + 5)
``````

The above code first defines the function f, and then passes the expression x + 5 to it. Excuse me, when should this expression be evaluated?

One opinion is call by value”, that is, before entering the function body, calculate the value of x + 5 (equal to 6), and then pass this value to the function f. C language uses this strategy.

``````
f(x + 5)
// 传值调用时，等同于
f(6)
``````

Another opinion is call by name”, that is, directly pass the expression x + 5 into the function body, and evaluate it only when it is used. The Hskell language uses this strategy.

``````
f(x + 5)
// 传名调用时，等同于
(x + 5) * 2
``````

Which one is better, call by value or call by name? The answer is that each has its pros and cons. Call by value is relatively simple, but when evaluating the parameter, the parameter is not actually used, which may cause performance loss.

``````
function f(a, b){
return b;
}

f(3 * x * x - 2 * x - 1, x);
``````

In the above code, the first parameter of the function f is a complex expression, but the function body is not used at all. Evaluating this parameter is actually unnecessary.

Therefore, some computer scientists tend to “call by name”, that is, only evaluate during execution.

## Second, the meaning of the Thunk function

The implementation of “call by name” of the compiler is usually to put the parameters in a temporary function, and then pass the temporary function into the function body. This temporary function is called the Thunk function.

``````
function f(m){
return m * 2;
}

f(x + 5);

// 等同于

var thunk = function () {
return x + 5;
};

function f(thunk){
return thunk() * 2;
}
``````

In the above code, the parameter x + 5 of the function f is replaced by a function. Wherever the original parameters are used, just evaluate the Thunk function.

This is the definition of the Thunk function, which is an implementation strategy of “call by name” to replace a certain expression.

## 3. Thunk function of JavaScript language

The JavaScript language is called by value, and its Thunk function has a different meaning. In the JavaScript language, the Thunk function replaces not an expression, but a multi-parameter function. Replace it with a single-parameter version, and only accept a callback function as a parameter.

``````

var Thunk = function (fileName){
return function (callback){
};
};
``````

In the above code, the readFile method of the fs module is a multi-parameter function, and the two parameters are the file name and the callback function. After the converter is processed, it becomes a single-parameter function, which only accepts the callback function as a parameter. This single-parameter version is called the Thunk function.

Any function, as long as the parameter has a callback function, can be written in the form of a Thunk function. Below is a simple Thunk function converter.

``````
var Thunk = function(fn){
return function (){
var args = Array.prototype.slice.call(arguments);
return function (callback){
args.push(callback);
return fn.apply(this, args);
}
};
};
``````

Use the above converter to generate the Thunk function of fs.readFile.

``````
``````

## Four, Thunkify module

For converters in the production environment, it is recommended to use the Thunkify module .

The first is installation.

``````
\$ npm install thunkify
``````

The usage is as follows.

``````
var thunkify = require('thunkify');
var fs = require('fs');

// ...
});
``````

The source code of Thunkify is very similar to the simple converter in the previous section.

``````
function thunkify(fn){
return function(){
var args = new Array(arguments.length);
var ctx = this;

for(var i = 0; i < args.length; ++i) {
args[i] = arguments[i];
}

return function(done){
var called;

args.push(function(){
if (called) return;
called = true;
done.apply(null, arguments);
});

try {
fn.apply(ctx, args);
} catch (err) {
done(err);
}
}
}
};
``````

Its source code mainly has a check mechanism, the variable called ensures that the callback function is only run once. This design is related to the Generator function below. Please see the example below.

``````
function f(a, b, callback){
var sum = a + b;
callback(sum);
callback(sum);
}

var ft = thunkify(f);
ft(1, 2)(console.log);
// 3
``````

In the above code, since thunkify only allows the callback function to be executed once, only one line of result is output.

## Five, generator function process management

You may ask, what is the use of the Thunk function? The answer is that it was really useless before, but ES6 has the Generator function, and the Thunk function can now be used for automatic process management of the Generator function. Take reading a file as an example. The following Generator function encapsulates two asynchronous operations.

``````
var fs = require('fs');
var thunkify = require('thunkify');

var gen = function* (){
console.log(r1.toString());
console.log(r2.toString());
};
``````

In the above code, the yield command is used to move the execution right of the program out of the Generator function, so a method is needed to return the execution right to the Generator function.

This method is the Thunk function, because it can return the execution power to the Generator function in the callback function. For ease of understanding, let’s first look at how to manually execute the above Generator function.

``````
var g = gen();

var r1 = g.next();
r1.value(function(err, data){
if (err) throw err;
var r2 = g.next(data);
r2.value(function(err, data){
if (err) throw err;
g.next(data);
});
});
``````

In the above code, the variable g is the internal pointer of the Generator function, indicating which step it is currently executing. The next method is responsible for moving the pointer to the next step and returning the information (value attribute and done attribute) of that step.

Looking carefully at the above code, you can find that the execution process of the Generator function is actually passing the same callback function to the value attribute of the next method repeatedly. This allows us to use recursion to automate this process.

## Six, automatic process management of Thunk function

The real power of the Thunk function lies in the automatic execution of the Generator function. The following is a Generator executor based on the Thunk function.

``````
function run(fn) {
var gen = fn();

function next(err, data) {
var result = gen.next(data);
if (result.done) return;
result.value(next);
}

next();
}

run(gen);
``````

The run function in the above code is an automatic executor of the Generator function. The internal next function is Thunk’s callback function. The next function first moves the pointer to the next step of the Generator function (gen.next method), and then judges whether the Generator function is over (result.done attribute), if it is not over, the next function is passed to the Thunk function (result.value attribute) ), otherwise just exit directly.

With this executor, it is much more convenient to execute the Generator function. No matter how many asynchronous operations there are, just pass in the run function directly. Of course, the premise is that every asynchronous operation must be a Thunk function, that is, the Thunk function must follow the yield command.

``````
var gen = function* (){