idiotype
idiotype

Reputation: 211

How can I wrap several bluebird promises in a promise?

I need an async wrapper for a redis query backed up by a db query. If the redis query fails, I want to make the db query. If the db query succeeds, I want to add the returned data to redis before returning. I need the function (hopefully one of several such methods on an object) to return a promise, since it will be called from within node.js. I am using bluebird promises library, and using it to promisfy redis. I am using mongo-gyro for the db, which is also bluebird based. Both of these work standalone.

Any help deeply appreciated - even pseudocode - esp. with erro handling

function get_something(key){
redis.get(key).done(function (res){
  if (null !== res){
    return res;  // how do I return a promise here?
  }
})
.done(function (res){
  db.find({'_id:key'}).done(function (res){
    if (null !== res){
      redis.set(key,result)  // set db value in redis
      .then(function(){
           return res;      //how do I return a promise here?
      })
    .catch()...?
    return res;  // how do I return a promise here?
    }
})
.catch...?

};

Updated: the function below works, with the final then displaying the data from redis or mongo. However - I have been unsuccessful so far in transforming this into a method on a class that returns a promise to be returned to node.js handler. NB - I needed to add the 'bind' so as to capture the source of the data

var oid = '+++++ test oid ++++++'
var odata = {
    'story': 'once upon a time'
}
var rkey = 'objects:'+ oid
redis.getAsync(rkey).bind(this).then(function(res){ 
  if(res === null){
    this.from = 'db'                            // we got from db
    return db.findOne('objects',{'_id':oid}) 
  }  
  data = JSON.parse(res)
  this.from = 'redis'                           // we got from redis
  return data
})
.then(function(res){    
  if(res !== null && this.from == 'db'){
    data = JSON.stringify(res)
    redis.setAsync(rkey,data)
  } 
  return res
})
.then(function(res){                           // at this point, res is not a promise
  console.log('result from ' + this.from)  
  console.log(res)                              
});

Upvotes: 1

Views: 767

Answers (2)

Roamer-1888
Roamer-1888

Reputation: 19288

Ideotype, from my understanding of your original question and your update, I believe that you can achieve your objective without the need a flag to track which source yielded the required data.

Something like this should work :

function get_something(oid) {
    var rkey = 'objects:' + oid;
    return redis.getAsync(rkey).then(function(res_r) {
        if (res_r === null) {
            return Promise.cast(db.findOne('objects', {'_id': oid})).then(function(res_db) {
                redis.setAsync(rkey, res_db).fail(function() {
                    console.error('Failed to save ' + rkey + ' to redis');
                });
                return res_db;
            });
        }
        return res_r;
    }).then(function (res) {//res here is the result delivered by either redis.getAsync() or db.find()
        if (res === null) {
            throw ('No value for: ' + rkey);
        }
        return res;
    });
}

Notes:

  • You may need to fix the lines with oid and rkey. My understanding here is limited.
  • The pattern here is unusual because the mongo-gyro query is optional and the subsequent redis update is academic regarding the success of the function as a whole.
  • The Promise.cast() wrapper may be unnecessary, depending on what is returned by db.findOne().
  • This will undoubtedly benefit from a once over by someone with a better understanding of Bluebird.

Upvotes: 1

Benjamin Gruenbaum
Benjamin Gruenbaum

Reputation: 276296

.done terminates a promise chain. Generally, Bluebird is smart enough to figure unhandled rejections on its own.

It's .then you're looking for:

redis.get(key).then(function(res){ res is redis .get response
     if(res === null) throw new Error("Invalid Result for key");
     return db.find({"_id":key); // had SyntaxError here, so guessing you meant this 
}).then(function(res){ // res is redis .find response
     return redis.set(key,result);
}).catch(function(k){ k.message === "Invalid Result for key",function(err){
   // handle no key found
});

Upvotes: 1

Related Questions