I hope that this answer can help you think about the concept of a "wrapper". Let's say we have a function divide:
const divide = (a,b) => a/b;
You can use this in normal code quite easily:
x = divide(10,5); // sets x to 2.
But you may decide that you care about the possibility of errors. In this case, division by zero is a possibility. You could certainly include some error handling code where you define the divide function. But you could also choose to "wrap" divide with an error handler. This way, we can keep the error handling aspects away from the main division logic. We would like to be able to define a safeDivide function like this:
const safeDivide = catchErrors(divide);
In the same way that divide is a function that takes two arguments, safeDivide also has to be a function that takes two arguments. So the catchErrors wrapper will have to return a function. We will start with something like this:
const catchErrors = (fn) => {
return (p,q) => fn(p,q);
}
If you pass a function fn to catchErrors, it will return a function. That returned function takes two arguments p and q, and returns fn(p,q). So far, this doesn't really achieve anything (except limiting the number of arguments). But now, we can add a try/catch block. I'll do it in a couple of steps, because the notation can be confusing.
The first step is to put an explicit return inside the inner arrow function.
const catchErrors = (fn) => {
return (p,q) => {
return fn(p,q);
}
}
This is technically the same code - it just looks slightly different. Now we add the try/catch.
const catchErrors = (fn) => {
return (p,q) => {
try {
return fn(p,q);
} catch (e) {
console.log("Error occurred. Continuing with null result.");
return null;
}
}
}
So now, the function returned from catchErrors will do the same as the original function, except when an exception is thrown, in which case it will return null. (I'm not saying that this is the best general way to handle exceptions, but it's useful as an example, and it's related to the original question.)
So now look again at where we use this catchErrors wrapper function.
const safeDivide = catchErrors(divide);
When you call the wrapper catchErrors with function divide, it doesn't actually do any dividing - it doesn't yet have any numbers to divide. Instead, it builds up a new function that, whenever it is called, would do the dividing, and catch any exception that arises. I hope that answers your first question.
Your second question is where req and res come from. They are names given to arguments that will be passed to the function. They can be passed to the wrapped function (along with a 3rd argument next), and they will also be passed to the inner (nameless) function which includes calls to Task.find and res.status(200). Those arguments will be provided by the Express (or other) web framework.
I will leave your 3rd question, on the async/await aspects of the wrapper, for another answer.