Node.js ~ Constructing Chained Sequence Of Promise Resolves
Solution 1:
Instead of something like this:
.then(function (rs) {
qry = '...';
req.query(qry)
.then(function (rss) {
you can use something like this:
.then(function (rs) {
qry = '...';
return req.query(qry);
}).then(function (rss) {
I.e. you can return a promise in one then
callback and get the resolved value of that promise in the next then
callback, so your indentation keeps constant.
Simpler example - instead of this:
a().then(va => {
b(va).then(vb => {
c(vb).then(vc => {
// you can use vc here
});
});
});
you can do:
a().then(va => {
returnb(va);
}).then(vb => {
returnc(vb);
}).then(vc => {
// you can use vc here
});
Or, even simpler if you use async
and await
:
va = await a();
vb = await b(va);
vc = await c(vb);
// you can use vc here
Note that you can only use await
inside of a function created with the async
keyword. In places where you don't have native support for async
and await
you can use Babel or with a slightly different syntax a generator based approach like in co
or Bluebird coroutines. For more info and support in browsers and Node, see this answer:
Update
This is not tested, but this is more or less how I would write it:
module.exports = {
dbConnection: function () {
return { user: 'sa', password: 'mypassword', server: 'localhost', database: 'mydb' };
},
CanIConnectToTheDB: function () {
var sql = require('mssql');
var myDao = require('./myDao');
var cn = new sql.ConnectionPool(myDao.dbConnection());
var req;
return cn.connect()
.catch(err =>Promise.reject('Error 1: ' + err))
.then(() => {
req = new sql.Request(cn);
var qry = 'select serverproperty(\'productversion\') as \'rs\'';
return req.query(qry)
.catch(err =>Promise.reject('Error 2: ' + err));
}).then(rs => {
var qry = 'select isnull(object_id(\'SomeObjectIKnowExists\'), -1)';
return req.query(qry)
.catch(err =>Promise.reject('Error 3: ' + err));
}).then(function (rss) {
return'CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS';
}).catch(err => {
// if you want it always resolved:return'CAN NOT CONNECT: ' + err;
});
}
};
Of course I would keep the final promise returned by that function to be rejected on errors and resolved only on success, but since you explicitly included that strange requirement in your question then I wrote it how you wanted.
But if it rejected the promise on any error then it would be much easier to use, especially if all you care is the answer to the question in the name of the function - Can I Connect To The DB:
CanIConnectToTheDB()
.then(() =>console.log("Yes I can"))
.catch(() =>console.log("No I can't"));
More info:
For more info see those answers:
- How to convert Waterfall method to promise
- jQuery: Return data after ajax call success (see explanation in updates to this answer)
Solution 2:
The key thing to remember about promises is that then
returns a new promise (as does catch
). How that new promise is settled depends on what you return from the handler: If you return a promise (or more generally, a thenable), the new promise from then
/catch
is resolved to the one that you returned (it will be fulfilled or rejected based on what that promise does); if you return a value, the new promise is fulfilled with that value. (FWIW, I go into promise terminology — "fulfill" vs. "resolve," etc. — in this post on my blog.)
So you can chain them together. Think of the then
and catch
handlers as transforming filters the ultimate result flows through.
Note also that if you have a starting point that gives you a promise (cn.connect()
), you don't need new Promise
: Just use then
and catch
to transform what passes through the chain by returning the (new) resolution value.
Another key thing to remember is that if a catch
handler returns a value, it converts a rejection into a resolution. To continue down the rejection path, a catch
handler must either throw an exception or return a promise that is/will be rejected.
Finally: require
calls should always be at the beginning of the module.
So, without removing your conversion of rejections to resolutions (more on that in a moment):
var sql = require('mssql');
var myDao = require('./myDao');
module.exports = {
dbConnection: function () {
return { user: 'sa', password: 'mypassword', server: 'localhost', database: 'mydb' };
},
CanIConnectToTheDB: function () {
var cn = new sql.ConnectionPool(myDao.dbConnection());
return cn.connect()
.then(function () {
var req = new sql.Request(cn);
var qry = 'select serverproperty(\'productversion\') as \'rs\'';
return req.query(qry)
.then(function (rs) {
qry = 'select isnull(object_id(\'SomeObjectIKnowExists\'), -1)';
return req.query(qry)
.then(function (rss) { // Note you're not using rss anywherereturn' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS';
})
.catch(function (err) {
return' CONNECTED// MASTER DB SUCCESS// ISSUE QUERYING MY DB //' + err + '//';
});
})
.catch(function (er) {
return' CONNECTED// COULD NOT QUERY MASTER DB //' + er + '//';
});
})
.catch(function() {
return' CAN NOT CONNECT';
});
}
};
Note: It IS my intention to NOT use the rej[ect] here. What you see guatanrees only the res[olve] returns. Meaning the code this returns to only needs one path with which to handle the returned value. Thereby the code returned to is simplier in it's flow.
Rejections follow a separate path from resolutions for a reason. It doesn't make things more complicated, it makes things simpler. Don't convert rejections into resolutions unless you're done an error-recovery of some kind and can continue on as though the rejection hadn't happened.
Here's that code where rejections are allowed to be rejections:
var sql = require('mssql');
var myDao = require('./myDao');
module.exports = {
dbConnection: function () {
return { user: 'sa', password: 'mypassword', server: 'localhost', database: 'mydb' };
},
CanIConnectToTheDB: function () {
var cn = new sql.ConnectionPool(myDao.dbConnection());
return cn.connect()
.then(function () {
var req = new sql.Request(cn);
var qry = 'select serverproperty(\'productversion\') as \'rs\'';
return req.query(qry)
.then(function (rs) {
qry = 'select isnull(object_id(\'SomeObjectIKnowExists\'), -1)';
return req.query(qry)
.then(function (rss) { // Note you're not using rss anywherereturn' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS';
});
});
});
}
};
Using it:
theModule.CanIConnectToTheDB()
.then(function() {
// Yes, let's do something
})
.catch(function() {
// No, report the problem, etc.
});
I'd also probably abstract out the bit I assume you'll end up doing over and over: Establishing a connection and getting a request object from it:
var sql = require('mssql');
var myDao = require('./myDao');
functiongetRequest() {
var cn = new sql.ConnectionPool(myDao.dbConnection());
return cn.connect().then(function() {
returnnew sql.Request(cn);
});
}
module.exports = {
dbConnection: function () {
return { user: 'sa', password: 'mypassword', server: 'localhost', database: 'mydb' };
},
CanIConnectToTheDB: function () {
returngetRequest().then(function(req) {
var qry = 'select serverproperty(\'productversion\') as \'rs\'';
return req.query(qry)
.then(function (rs) {
qry = 'select isnull(object_id(\'SomeObjectIKnowExists\'), -1)';
return req.query(qry)
.then(function (rss) { // Note you're not using rss anywherereturn' CONNECTED// MASTER DB SUCCESS// MY DB SUCCESS';
});
});
});
}
};
Post a Comment for "Node.js ~ Constructing Chained Sequence Of Promise Resolves"