Handle successful HTTP promise if one fails in Bluebird - javascript

I'm fairly new to Promises and have been attempting to get this piece of code to work properly. Here's what I have.
var Promise = require('bluebird');
Promise.join(getProducts, getPricing, function(products, pricing) {
console.log('products: ' + products.body);
console.log('pricing: ' + pricing.body);
// Add pricing to products here
res.send(products.body);
})
.catch(function(e) {
console.log(e);
res.sendStatus(500);
});
Requirements...
I want to call both APIs at the exact same time and process the results when both complete.
If the Pricing API fails, I still want to return the Products with no pricing.
If the Products API fails, I want to error out and send 500 back to the client.
My code seems to work if both API calls are successful, but will always go into the catch if either fail and ignore any successful calls.
I can get this working fine if I use a synchronous Promise chain, but I'd like to call both APIs at the same time.
How can I call both APIs asynchronously and process the results outside of the catch?

You'll want to put a catch on exactly the promise whose failure you want to handle - i.e. getPricing.
Promise.join(getProducts, getPricing.catch(function(err) {
// ignore or log?
return null;
}, function(products, pricing) {
console.log('products: ' + products.body);
if (pricing) {
console.log('pricing: ' + pricing.body);
// Add pricing to products here
} // else?
res.send(products.body);
}).catch(function(e) {
console.log(e);
res.sendStatus(500);
});
You can also use getPricing.reflect() if you want to more explicitly distinguish the two cases.

Forewarning: I'm decent with promises, but not this particular library.
var Promise = require('bluebird');
var pricing = getPricing().catch(function(e) {
return null;
})
Promise.join(getProducts, pricing, function(products, pricing) {
console.log('products: ' + products.body);
console.log('pricing: ' + pricing ? pricing.body : '<null>');
// Add pricing to products here
res.send(products.body);
}).catch(function(e) {
console.log(e);
res.sendStatus(500);
});

Modify getPricing() such that it never fails. The easiest way to do this is by adding a top-level .catch(). The example code below wraps the getPricing() method
var Promise = require('bluebird');
function getProducts(){/* add implementation */};
function originalGetPricing(){/* add implementation */};
function getPricing(){
return originalGetPricing()
.catch(function(err) {
return Promise.resolve(/* replace this comment with empty object or some other suitable response */);
});
};
Promise.join(getProducts, getPricing, function(products, pricing) {
console.log('products: ' + products.body);
console.log('pricing: ' + pricing.body);
// Add pricing to products here
res.send(products.body);
})
.catch(function(e) {
console.log(e);
res.sendStatus(500);
});

Related

How to make the later Promises wait for the first Promise to finish in bluebird?

I'm trying to chain Promises. Here's the code
crawler.cache(url).then(function (cRes) {
console.log('get from cache');
if(cRes) {
return Promise.resolve(JSON.parse(cRes));
} else {
console.log('no cache');
crawler.db(url).then(function (result) {
console.log('get from db');
return Promise.resolve(JSON.parse(result));
});
}
}).then(function fetch(someResult) {
console.log('fetch data from' + someResult);
});
What I'm hoping is that it would look into the cache first if it couldn't find then it should look in to db. And then pass the result to the last then which to fetch the result.
But here's what printing from my console.
get from cache
no cache
fetch data from null
get from db
First it couldn't find anything in the cache then it's trying to fetch from the database but it didn't wait until getting from the db is done it went on to fetch data.
How do I solve this problem when the last then has to wait for everything above it to finish to get the result then it can do the next operation?
You can just return the promise inside the then continuation, this will cause the parent promise to wait until the returned promise is fulfilled and adopt it's value.
There is also no need to use the Promise.resolve method, you can just return the value directly.
crawler.cache(url).then(function (cRes) {
if (cRes) {
console.log('cache result');
return JSON.parse(cRes);
} else {
console.log('no cache');
return crawler.db(url).then(function (result) {
return JSON.parse(result);
});
}
}).then(function fetch(someResult) {
console.log('fetch data from' + someResult);
});
Bonus:
If you are storing your cache in memory. you probably don't need to return a promise from your crawler.cache function.
OT: I'd recomend you a slightly different approach. Use the rejection-path of the promises to communicate that a Value is not available/couldn't be resolved or provided.
This way it would be more intuitive wether a (possibly falsy) value is provided or, wether we have to resolve that from a different source like the db.
Sth. like (pseudocode):
crawler.cache = function(url){
//you will have to adapt this part to your structure.
//but you get the idea?
return new Promise(function(resolve, reject){
if(url in _cache){
resolve( _cache[url] );
}else{
//you can simply reject() without a reason,
//or you can return an Error-Object, or some message, or whatever ...
reject({ message: "no cache for " + url });
}
});
}
//try to access the value from the cache
crawler.cache(url)
//if the cache fails (to provide you a value),
//try to resolve the value from the db
.catch(() => crawler.db(url).then(saveToCache))
//parse the result, wether it is from the cache or the db
//maybe you want to add a second argument to catch the case where the db also fails?
.then(str => JSON.parse(str));
//maybe you want to add a catch here,
//in case JSON.parse is provided sth false, like a damaged string?

Chek the return from a promise function before proceeding. Wrong approach?

Background: I have a PHP background and this is my first application using MEAN stack.
I need to save a record but before I must to check if there is any record under the same id already saved in the DB.
In PHP I would do something like this:
Once the user clicks "Save":
1) Call the function to check if an entry with that id already exists
2) If it doesnt, call the save function.
In Javascript, I'm getting a little confused with Promises and so on.
Can somebody give me some light here?
Right now, I'm doing the following:
In the save api, I call this function to check if the record already exists in the DB:
recordExists = findTranscationByBill(billId);
function findTransactionByBill(billId){
results = new promise(function(resolve, reject){
Transactions.find({billId : billId},function(err, transactions){
if(err)
reject("Error: "+err);
//console.log(transactions);
resolve(transactions);
});
});
results.then(function(data){
console.log('Promise fullfilled: '+ data);
}, function(error){
console.log('Promise rejected: ' + error);
});
return $results;
}
The problem is that I think I'm not using promise properly, as my variable doesn't get populated (because its Async).
In the console.log I see that the promise is being fulfilled however, the variable returns as [object Object]
I'm stucked with this problem because I don't know if I should carry on thinking as PHP mindset or if there is a different approach used in Javascript.
Thanks in advance!
In my opinion you could just as well use a callback for this, and since MongoDB has a count method, why not use it
function findTransactionByBill(billId, callback){
Transactions.count({billId : billId}, function(err, count){
if (err) {
callback(err, false);
} else {
callback(null, count !== 0);
}
});
}
and to use it
findTransactionByBill(billId, function(err, exists) {
if (err) {
// handle errors
} else if ( ! exists ) {
// insert into DB
}
}
I think the right function is:
function findTransactionByBill(billId){
var results = new promise(function(resolve, reject){
Transactions.find({billId : billId},function(err, transactions){
if(err) {
reject(err);
} else {
if (transactions.length === 0) {
reject('No any transaction');
} else {
//console.log(transactions);
resolve(transactions);
}
});
});
results.then(function(data){
console.log('Promise fullfilled: '+ data);
}, function(error){
console.log('Promise rejected: ' + error);
});
return results;
}
And then use it like this:
recordExists = findTranscationByBill(billId);
recordExists.then(function() {
// resolved, there are some transactions
}, function() {
// rejected. Error or no any transactions found
// may be you need to check reject result to act differently then no transactions and then error
});
I assume you are using mongodb native drive.
I think mongodb doesn't have promise built-in supported. So you have to promisify it by a little help from promise library. Please refer this if you want to use bluebird.
After promisifying, the code should looks like that (using bluebird):
Promise = require('bluebird');
// Promisify...
var _db = null;
var client = MongoClient.connectAsync('mongodb://localhost:27017/test')
.then(function(db) {
_db = db
return db.collection("myCollection").findOneAsync({ id: 'billId' })
})
.then(function(item) {
if (item)
_db.save(item);
})
.catch (err) {
// error handling
}
The above code is not perfect, because it introduced a global var, so the better version may be
Promise = require('bluebird');
// Promisify...
var client = MongoClient.connectAsync('mongodb://localhost:27017/test')
.then(function(db) {
return Promise.prop({
item: db.collection("myCollection").findOneAsync({ id: 'billId' },
db: db
})
})
.then(function(result) {
var item = result.item;
var db = result.db
if (item)
db.save(item);
})
.catch (err) {
// error handling
}
You need to check bluebird to know how to use it. Also they are many other promise libraries like q, when, but all are similar stuff.

How to call Q promise notify within the promise chain

I need helps on notify() within the promise chain.
I have 3 promise base functions connect(), send(cmd), disconnect(). Now I would like to write another function to wrap those call in following manner with progress notification.
function bombard() {
return connect()
.then(function () {
var cmds = [/*many commands in string*/];
var promises = _.map(cmds, function (cmd) {
var deferred = Q.defer();
deferred.notify(cmd);
send(cmd).then(function (result) {
deferred.resovle(result);
});
return deferred.promise;
});
return Q.all(promises);
})
.finally(function () { return disconnect() })
}
Run the function like that
bombard.then(onResolve, onReject, function (obj) {
console.log(ob);
});
I supposed I will get notification for every command I have sent. However, it does not work as I expected. I get nothing actually.
Although I believe this is due to those notifications havn't propagated to outside promise, I have no idea on how to propagated those notifications on Q or wrapping that promise chain: connect, send, disconnect in a one deferred object.
Thanks
I have some good news and some bad news.
Very good! You have found out the problem with the notifications API and why it is being removed in Q in the v2 branch, being deprecated in newer libraries like Bluebird, and never included in ECMAScript 6. It really boils down to the fact promises are not event emitters.
The notifications API does not compose or aggregate very well. In fact, notifications being on promises does not make too much sense imo to begin with,.
Instead, I suggest using a progress notification even, kind of like IProgress in C#. I'm going to simulate all the actions with Q.delay() for isolation, your code will obviously make real calls
function connect(iProgress){
return Q.delay(1000).then(function(res){
iProgress(0.5,"Connecting to Database");
}).delay(1000).then(function(res){
iProgress(0.5,"Done Connecting");
});
}
function send(data,iProgress){
return Q.delay(200*Math.random() + 200).then(function(res){
iProgress(0.33, "Sent First Element");
}).delay(200*Math.random() + 400).then(function(){
iProgress(0.33, "Sent second Element");
}).delay(200*Math.random() + 500).then(function(){
iProgress(0.33, "Done sending!");
});
}
// disconnect is similar
Now, we can easily decide how our promises compose, for example:
function aggregateProgress(num){
var total = 0;
return function(progression,message){
total += progression;
console.log("Progressed ", ((total/num)*100).toFixed(2)+"%" );
console.log("Got message",message);
}
}
Which would let you do:
// bombard can accept iProgress itself if it needs to propagate it
function bombard() {
var notify = aggregateProgress(cmds.length+1);
return connect(notify)
.then(function () {
var cmds = [/*many commands in string*/];
return Q.all(cmds.map(function(command){ return send(command,notify); }));
});
}
Here is a complete and working fiddle to play with

Testing ajax requests using jasmine returns TypeError

The description of the task. I want to test the code that loads a list of resources using $.get.
So, the source code:
fetchTemplates: function(list, cb){
var promises = [],
$container = $('#templates');
Object.keys(list).forEach(function(tplSelector){
if($(tplSelector).length > 0){ return; }
var promise = $.get(list[tplSelector]);
promise
.done(function(tplHtml){
$container.append(tplHtml);
})
.fail(function(){
console.warn('Template "' + tplSelector + " not found by url:" + list[tplSelector]);
});
promises.push( promise );
});
return $.when.apply($,promises).done(cb);
}
The test suite:
it("Correct template fetching", function (done) {
var fetchResult = viewManager.fetchTemplates({
'#helpTpl': 'somecorrectaddress'
});
fetchResult.done(function () {
expect(true).toBeTruthy();
done();
});
fetchResult.fail(function () {
expect(false).toBeTruthy();
done();
});
});
What it generates. Test passes, but generates an error:
TypeError: 'null' is not an object (evaluating 'this.results_.addResult')
at jasmine.js?2348
So, the test case marks as passed. But whole test suite still generates the error above (and this method is the only one async, other parts are trivial to test). My thought was that since the tested method contains async operations and promises - results were not properly handled and thus TypeError. So I added jasmine async "done()" to handle the issue - unfortunately nothing changed. Also worth noting that if I leave only one test in the suite using "iit" - no error is generated. Search didn't find similar cases. Any ideas?
You need to wrap your async calls in 'runs()' with 'waitsFor()' after, read the documentation here. I've never worked with jquery, but try something like the following inside your it function:
var done = false;
var that = this;
runs( function() {
//your async test goes here
that.done = true;
});
waitsFor( function() {
return that.done;
}, "async code failed", 2000);

Using JS Promises to Execute Multiple Async Queries to Build an Object

After recently discovering JS promises, I have been studying them so that I might build a certain functionality that allows me to execute 4 async queries, use the result of each to build an object that I can finally send as a response to a request directed at my node app.
The final object is made up of 3 array properties containing the resulting rows of each query.
It seems that I've done something wrong handling the promises, though, because ultimately, game is not being built. It is sent as an empty object. Here's a JSFiddle.
What is my mistake?
Here's what I have so far:
function sendGame(req, res, sales, settings, categories) {
var game = new Object();
game.sales = sales;
game.settings = settings;
game.categories = categories;
JSONgame = JSON.stringify(game);
res.writeHead(200, {
'Access-Control-Allow-Origin': 'http://localhost',
'Content-Length': JSONgame.length,
'Content-Type': 'application/json'
});
res.write(JSONgame);
res.end();
console.log('Game: ' + JSON.stringify(game, null, 4));
console.log('--------------------------------------');
console.log('User ' + req.body.username + ' successfully retrieved game!');
}
function retrieveSales(req, connection, timeFrame) {
console.log('User ' + req.body.username + ' retrieving sales...');
connection.query('select * from sales_entries where date BETWEEN ? AND ?', timeFrame,
function (err, rows, fields) {
if (err) {
callback(new Error('Failed to connect'), null);
} else {
sales = [];
for (x = 0; x < rows.length; x++) {
sales.push(rows[x]);
}
//console.log('Sales: ' + JSON.stringify(sales, null, 4));
return sales;
}
});
}
retrieveCategories() and retrieveSettings() omitted for readability; they are the same as retrieveSales() mostly.
function gameSucceed(req, res) {
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment().days(0).format("YYYY-MM-DD HH:mm:ss"), moment().days(6).format("YYYY-MM-DD HH:mm:ss")];
var connection = createConnection();
connection.connect(function (err) {
if (err) return callback(new Error('Failed to connect'), null);
console.log('Connection with the Officeball MySQL database openned for game retrieval...');
var sales = retrieveSales(req, connection, timeFrame);
var settings = retrieveSettings(req, connection);
var categories = retrieveCategories(req, connection);
var all = q.all([sales, settings, categories]);
all.done(function () {
sendGame(req, res, sales, settings, categories);
});
});
}
Your problem is that you're not using promises. All your APIs use callbacks.
A promise is like a closed box:
A promise also has a method that opens the box, works on the value and returns another box on the value (also opening any additional boxes along the way). That method is .then:
In boxes, it does:
=>( . => ) =>
That is, it adds a handler that gets an open box and returns a box. Everything else just combines stuff. All .all does is wait for a list of promises to resolve, it is exactly like .then in the fact it waits for a result. Because promises are boxes, you can pass them around and return them which is very cool.
Generally:
Whenever you return from a promise handler (not a rejection), you are fullfilling it indicating normal flow continuation.
Whenever you throw at a promise handler, you are rejecting indication exceptional flow.
So basically in node speak:
Whenever you returned a null error and a response, you resolve the promise.
Whenever you returned an error and no response, you reject the promise.
So:
function myFunc(callback){
nodeBack(function(err,data){
if(err!== null){
callback(new Error(err),null);
}
callback(data+"some processing");
})
});
Becomes:
function myFunc(){
return nodeBack().then(function(data){ return data+"some processing"; });
}
Which I think is a lot clearer. Errors are propagated across the promise chain just like in synchronous code - it's very common to find synchronous analogs to promise code.
Q.all takes a list of promises and waits for them to complete, instead you want Q.nfcall to transform a callback based API to a promise one and then use Q.all on that.
That is:
var sales = Q.nfcall(retrieveSales,req, connection, timeFrame);
var settings = Q.nfcall(retrieveSettings,req, connection);
var categories = Q.nfcall(retrieveCategories, req, connection);
Q.nfcall takes a nodeback in the err,data convention and converts it to a promise API.
Also, when you do
return sales;
You are not really returning anything, since it returns synchronously. You need to use callback like in your error case or promisify it altogether. If you don't mind, I'll do it with Bluebird since it comes with much better facilities for dealing with these interop cases and does so much much faster, if you'd like you can switch promisifyAll for a bunch of Q.nfcall calls.
// somewhere, on top of file
connection = Promise.promisifyAll(connection);
// note I'm passing just the username - passing the request breaks separation of concerns.
var retrieveSales = Promise.method(username, connection, timeFrame) {
console.log('User ' + username + ' retrieving sales...');
var q = 'select * from sales_entries where date BETWEEN ? AND ?';
return connection.queryAsync(q, timeFrame).then(function(rows, fields){
return rows;
});
}
Note that suddenly you don't need a lot of boilerplate for making a query, you can use queryAsync directly instead if you'd like.
Now the code that wraps it becomes:
var gameSucceed = Promise.method(function gameSucceed(req, res) {
console.log('User ' + req.body.username + ' retrieving game...');
var timeFrame = [moment()....];
var connection = Promise.promisifyAll(createConnection());
return conn.connectAsync().then(function () {
console.log('Connection with the ...');
//sending req, but should really be what they use.
return Promise.all([retrieveSales(req,conn,timeFrame),
retrieveSettings(req,conn),
retrieveCategories(req,conn)]);
});
});
Now you can call sendGame(req, res, sales, settings, categories); outside of gameSucceed which doesn't hide what it does as much -
gameSucceed(req,res).spread(function(sales,settings,cats){
return sendGame(req,res,sales,settings,cats);
});

Categories

Resources