NodeJS query processing results design pattern avoid nested then statements

100
May 31, 2019, at 01:10 AM

I am writing an application in NodeJS10 and Mongoose5. I am writing a custom server-side pagination function. I want to run the pagination in a controller for separation of concerns and reusability.

I have a query to get data and return it in a then() response handler. I am then calling the custom pagination function inside the then() handler. This works, but I am concerned about the "nested" then/catch patterns. I think this might be messy especially if there is any additional processing required on the data before returning the response.

Here is the "nested" structure. How can I improve this to make it no longer nested and more readable while still catching all possible errors and returning the response correctly? Note that this works just fine, it's just the design pattern is messy and I want to avoid the "nested" then() statements if possible.

router.get('/:id', (req, res, next) => {
  const origin = 'routes/items.js GET /:id';
  const id = req.params.id;
  items.find({
    _id: id
  }).then(data => {
    uiCtrl.pagination(data, req.query).then((result) => {
      res.status(200).send(result);
    }).catch((error) => {
      console.error(`pagination failed ${error}`);
      res.status(500).send(`pagination failed ${error}`);
    });
  }).catch((error) => {
    console.log(`${origin} ${error}`);
    res.status(404).send('Not found');
  });
});

Here is the custom async function being called inside the query result above.

const uiCtrl = {};
uiCtrl.pagination = async (data, query) => {
  // pagination logic here
  return data;
}
module.exports = uiCtrl;
Answer 1

Here is a rewrite using normal async/await stuff:

var data, results;
try {
   data = await items.find({_id:id}).toArray();
}
catch (err) {
   console.log(`${origin} ${error}`);
   res.status(404).send('Not found');
}
try {
   results = await uiCtrl.pagination(data, req.query);    
}
catch (err) {
   console.error(`pagination failed ${error}`);
   res.status(500).send(`pagination failed ${error}`);
}

It should be noted that given the fact that I know nothing more of your code than what I can see, there are probably some other things you can do that will make the code better to read and easier to use (ie rejecting a promise and doing something meaningful with it instead of throwing exceptions). You would also need to have all this code wrapped in an async method so you can the await keyword.

EDIT

For completion's sake given the full code now I would start by rewriting it something like this:

router.get('/:id', async (req, res, next) => {
  const origin = 'routes/items.js GET /:id';
  const id = req.params.id;
  var itemResults, paginationResults;
  try {
     itemResults = await items.find({_id: id}).toArray();
  }
  catch (err) {
     console.log(`${origin} ${error}`);
     res.status(404).send('Not found');
  }
  try {
     paginationResults = await uiCtrl.pagination(data, req.query);
     res.status(200).send(result);
  }
  catch (err) {
     console.error(`pagination failed ${error}`);
     res.status(500).send(`pagination failed ${error}`);
  }
});

One other thing I would do as a sort of optimization or housekeeping thing would be to move the error catching into a single catch block and parse the error out there to make things more concise.

Also including this as it talks about something called callback hell which is very relevant to the information you are asking about.

http://callbackhell.com/

Answer 2

Here is the reworked solution based on the accepted answer. I was getting this error, I think this is due to placing the 'async' keyword directly in the router definition e.g. router.get('/', async (req, res, next). Perhaps because in this format it is not handled with then or catch.

(node:3564) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1) (node:3564) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I ended up with a slightly modified solution, I made a wrapper function and called it with a then() handler. Thanks!

router.get('/', (req, res, next) => {
  const origin = 'routes/items.js GET /';
    async function queryWrapper() {
        let data, results;
        try {
          data = await items.find();
          console.log(`data ${data}`);
        }
        catch (err) {
          console.log(`${origin} ${err}`);
        }
        try {
          results = await uiCtrl.pagination(data, req.query);
        }
        catch (err) {
          console.error(`pagination failed ${err}`);
        }
        if (results) {
          return results;
        } else {
          return false;
        }
      }
      queryWrapper().then((result) => {
        res.status(200).send(result);
      }).catch((error) => {
        console.error(`${error}`);
        res.status(500).send(`${error}`);
      });
});
Rent Charter Buses Company
READ ALSO
How to create room using websocket?

How to create room using websocket?

How to create an alternative socketio room using ws websocket?

132
calculate date-time difference in NodeJS

calculate date-time difference in NodeJS

I am getting this as follow

210
Does RegExp behave differently on different environments?

Does RegExp behave differently on different environments?

I've created a Restful API in node that checks for words within a sentenceThe code works while running locally

112
How to write a tensorflowjs-dependent library that will import from either tfjs or tfjs-node appropriately

How to write a tensorflowjs-dependent library that will import from either tfjs or tfjs-node appropriately

I am writing a TypeScript library that is basically a clone of the python tensorflow-probability packageI am intending to use this library in other projects, both server-side and in the browser

137