22

I'm new to Node.js, so I'm still wrapping my head around asynchronous functions and callbacks. My struggle now is how to return a response after reading data from a file in an asynchronous operation.

My understanding is that sending a response works like this (and this works for me):

app.get('/search', function (req, res) {
    res.send("request received");
});

However, now I want to read a file, perform some operations on the data, and then return the results in a response. If the operations I wanted to perform on the data were simple, I could do something like this -- perform them inline, and maintain access to the res object because it's still within scope.

app.get('/search', function (req, res) {
    fs.readFile("data.txt", function(err, data) {
        result = process(data.toString());
        res.send(result);
    });
});

However, the file operations I need to perform are long and complicated enough that I've separated them out into their own function in a separate file. As a result, my code looks more like this:

 app.get('/search', function (req, res) {
    searcher.do_search(res.query);
    // ??? Now what ???
});

I need to call res.send in order to send the result. However, I can't call it directly in the function above, because do_search completes asynchronously. And I can't call it in the callback to do_search because the res object isn't in scope there.

Can somebody help me understand the proper way to handle this in Node.js?

ekl
  • 1,052
  • 2
  • 9
  • 24
  • I would say, either pass the `res` object as a parameter into the function, or make it so you can pass a function as a parameter and then call it when the work is complete (i.e. create your own callback). The latter would probably be better, as it doesn't couple your code to Express' API. – Joe Clay Apr 25 '16 at 10:34

2 Answers2

19

To access a variable in a different function, when there isn't a shared scope, pass it as an argument.

You could just pass res and then access both query and send on the one variable within the function.


For the purposes of separation of concerns, you might be better off passing a callback instead.

Then do_search only needs to know about performing a query and then running a function. That makes it more generic (and thus reusable).

searcher.do_search(res.query, function (data) {
    res.send(...);
});

function do_search(query, callback) {
    callback(...);
} 
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 3
    Passing a callback is definitely the way to go. +1 for suggesting. – Lars de Bruijn Apr 25 '16 at 11:07
  • Thanks for the help. I did end up modifying my do_search function to accept a callback, and it works great. – ekl Apr 25 '16 at 13:38
  • Does this answer the question? Why doesn't the example include `app.get`, which is the function in question? – Brent Bradburn Feb 27 '18 at 15:50
  • @nobar — Yes. Because there is no point in copying a bunch of code out of the question verbatim; that would just make it harder to spot the differences. – Quentin Feb 27 '18 at 15:53
4

The existing answers are perfectly valid, you can also use async/await keywords since ES2017. Using your own function:

app.get('/search', async(req, res, next) {
  try {
    const answer = await searcher.do_search(req.query);
    res.send(answer);
  }
  catch(error) {
    return next(error);
  }
});
Echo C
  • 65
  • 6