0
getCount: (filter = null) => {
  var whereConditions = {};
  if(filter != null) whereConditions.role = filter;

  User
    .count({
      where: whereConditions,
    })
    .exec((err, n) => {
      console.log(n);
      return n;
    });
}

The method above returns undefined when called, but when I console.log n, I get the correct output. How do I fix this?

Nicholas Kajoh
  • 1,451
  • 3
  • 19
  • 28
  • 1
    Possible duplicate of [How do I return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-do-i-return-the-response-from-an-asynchronous-call) – nem035 May 22 '17 at 14:00

1 Answers1

2

where() should return a promise which means you can use then() and catch() in the code calling getCount(filter) of your "service" to access the successful response of values and errors as necessary. According to the documentation, .exec() can directly be replaced with then() and catch(). Try something like the following:

Service:

getCount: (filter = null) => {
  var whereConditions = {};
  if(filter != null) whereConditions.role = filter;

  // return promise
  // using this value directly will not allow you to access queried values
  return User.count({ where: whereConditions });
}

Controller/Caller:

this.someService.getCount('someFilterValue')
  .then(values = {
      console.log(values);
      // do something with values like bind to variable/property of the view
  })
  .catch(error => console.log(error));

Or depending on your structure, you could try something like this to do everything inside of getCount():

getCount: (filter = null) => {
  var whereConditions = {};
  if(filter != null) whereConditions.role = filter;

  // return promise
  // using this value directly will not allow you to access queried values
  return User
      .count({ where: whereConditions })
      .then(values => res.json(values))
      .catch(error => res.serverError(error));
}

You can delegate the functionality in the then() of getCount() to a separate method to reduce repetition:

 getCount: (filter = null) => {
  var whereConditions = {};
  if(filter != null) whereConditions.role = filter;

  // return promise
  // using this value directly will not allow you to access queried values
  return User
      .count({ where: whereConditions })
      .then(handleResponse)
      .catch(error => res.serverError(error));
},

handleResponse: (data) => {
  // do something with data
  return data.map(e => e.someProperty.toUpperCase());
}

You can chain then() as necessary to continue transforming values as needed each time returning the transformed values. You can delegate this to methods as long as you continue returning values. This would also allow you take async actions and only acting on them once they are resolved.

getCount: (filter = null) => {
  var whereConditions = {};
  if(filter != null) whereConditions.role = filter;

  // return promise
  // using this value directly will not allow you to access queried values
  return User
      .count({ where: whereConditions })
      .then(values => values.map(e => e.someProperty.toUpperCase()))
      .then(transformedValues => transformedValues.filter(e => e.indexOf(filter) > -1))
      .then(filteredValues => res.json(filteredValues))
      .catch(error => res.serverError(error));
}

Hopefully that helps!

Alexander Staroselsky
  • 37,209
  • 15
  • 79
  • 91
  • Do you mean the result of `return User.count({ where: whereConditions });` return `[object Promise]`? Or do you mean that the `console.log(values)` inside of the `then()` returned that result? Yes it would return that. Use `then()` as the example provided to act on the returned/resolved query values in the view/controller or whether you need to access those. Using `then()` in the calling code is the most effective way to handle the async nature of the query. Perhaps provide the example of how you are calling `getCount()` in your application. – Alexander Staroselsky May 22 '17 at 14:31
  • I've provided an updated approach to handling the query results using `then()` and `catch()` as well as sending the values directly from `getCount()`. – Alexander Staroselsky May 22 '17 at 14:37
  • `.then([...]).catch([...])` works fine in my controller, but returns `undefined` when placed in my model. Thing is, I need to call `getCount()` several times. There'd be a lot of repetition if this code isn't in the model. – Nicholas Kajoh May 22 '17 at 15:01
  • Each time `count()` is called it creates a new query instance. If you are calling `getCount()` multiple times, you can delegate the `then()` and `catch()` work to a method rather than replicating the code in `then()`. That being said you will need to use `then()` each time you call `getCount()`. I've updated my answer for a possible approach. If you need to share the results of `getCount()` within other functions/services/objects, then you probably don't want to be calling `getCount()` multiple times. – Alexander Staroselsky May 22 '17 at 15:04
  • Decided to add the `handleResponse` callback as a param in `getCount`. So in my controller, I did `var usersCount; User.getCount(n => {usersCount = n;}, "customer");`. Works that way. Was that what you had in mind? – Nicholas Kajoh May 22 '17 at 15:33