简体   繁体   中英

How to remove the boilerplate code when using q in node.js

I am currently using q with Node.js . All my models use the promises from q . Later I realized writing a lot of boilerplate code like this

count: function(){
  var deferred = Q.defer()
  User.count(function(err,count){
    if(err)
      deferred.reject(err)
    else
      deferred.resolve(count)
  })
  return deferred.promise;
},
findAll: function(){
  var deferred = Q.defer()
  User.find({}, function(err,users){
    if(err)
      deferred.reject(err)
    else
      deferred.resolve(users)
  })
  return deferred.promise;
}

Is there a way remove this boilerplate code?

Is there a way remove this boilerplate code?

Yes, Q has dedicated helper functions for interfacing with node callbacks .

You can simplify your code to

count: function(){
  return Q.nfcall(User.count);
},
findAll: function(){
  return Q.nfcall(User.find, {});
}

If those methods rely on their this value, you can use Q.ninvoke(User, "count") and Q.ninvoke(User, "find", {}) .

You can even take this to a higher extreme and avoid the function expression completely, by using Q's function/method binding:

count: Q.nfbind(User.count),
findAll: Q.nfbind(User.find, {})

or with a this value:

count: Q.nbind(User.count, User),
findAll: Q.nbind(User.find, User, {})

But notice that with these solutions you need to ensure that count and findAll are called with zero arguments.

Note: Q has a built-in version of the below, so if you're using Q (as the OP is), see Bergi's answer . If you're using some other promise lib, see if it does; if not, see below.


You can give yourself a utility function that wraps Node-style calls and turns them into promises (but keep reading), so:

count: function() {
  return makePromise(User.count)
}

makePromise would look something like

function makePromise(f) {
  var deferred = Q.defer()
  var args = Array.prototype.slice.call(arguments)
  args[0] = function(err) {
    if(err)
      deferred.reject(err)
    else
      deferred.resolve(Array.prototype.slice.call(arguments, 1))
  }
  f.apply(null, args)
  return deferred.promise
}

(I'm trying to follow your convention of leaving off ; that ASI will correct)

That calls the function you gave it, passing along any further arguments, and when it gets the response either rejects the promise using err , or resolves it using an array of the arguments it got after err . Could be used with all Node-style functions that call their callbacks with err as the first argument.

But , someone's already done that for you: promisify :-) I'm fairly sure there's at least one other one as well.

A slightly more complex version would support optionally giving a thisArg up front:

function makePromise(obj, f) {
  var deferred = Q.defer()
  var args, func, thisArg;
  if (typeof obj === "function") {
    thisArg = null
    func = obj
    args = Array.prototype.slice.call(arguments)
  } else {
    thisArg = obj
    func = f
    args = Array.prototype.slice.call(arguments, 1)
  }
  args[0] = function(err) {
    if(err)
      deferred.reject(err)
    else
      deferred.resolve(Array.prototype.slice.call(arguments, 1))
  }
  func.apply(thisArg, args)
  return deferred.promise
}

Then if User.count needs to be called with this = User :

count: function() {
  return makePromise(User, User.count)
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM