Reputation: 211
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
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:
oid
and rkey
. My understanding here is limited.Promise.cast()
wrapper may be unnecessary, depending on what is returned by db.findOne()
.Upvotes: 1
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