What is the promise disposer pattern?
The issue with your code
The problem with the above approach is that if you forget releasing the connection after every single time you perform getDb
you have a resource leak that might freeze your app eventually when it runs out of the resource you're leaking.
You might, in one place do:
var users = getDb().then(function(conn){
return conn.query("SELECT name FROM users");
});
Which will leak a database connection that was never closed.
The disposer pattern
The disposer pattern is a way to couple a scope of code with owning the resource. By binding the resource to a scope we make sure it is always released when we're done with it and we can't easily forget to release it. It is similar to using
in C#, with
in Python and try-with-resource in Java as well as RAII in C++.
It looks like:
withResource(function(resource){
return fnThatDoesWorkWithResource(resource); // returns a promise
}).then(function(result){
// resource disposed here
});
Applying it here
If we wrote our code as:
function withDb(work){
var _db;
return myDbDriver.getConnection().then(function(db){
_db = db; // keep reference
return work(db); // perform work on db
}).finally(function(){
if (_db)
_db.release();
});
}
We could write our above code as:
withDb(function(conn){
return conn.query("SELECT name FROM users");
}).then(function(users){
// connection released here
});
Examples of users of the disposer pattern are sequelize and knex (bookshelf's query builder). It's also possible to use it for simpler things like hiding a loader when all AJAX requests completed for instance.
How to dispose resources in rejected promises with bluebird?
This question almost answers itself, in that "promise" and "dispose" together beg you to consider the Promise Disposer Pattern, which is actually nothing more than judicious use of .then()
or .finally()
with a callback that disposes/closes something that was created/opened earlier.
With that pattern in mind, you will find it simpler to orchestrate disposal in pingPort()
rather than findPort()
.
Assuming the code in the question to be broadly correct, you can chain finally()
as follows :
var pingPort = function(port) {
var serial; // outer var, necessary to make `serial` available to .finally().
return new promise(function(resolve, reject) {
serial = new com.SerialPort(port.comName, {
baudrate: 19200,
parser: com.parsers.readline(lineEnd)
}, false);
serial.on('data', function(data) {
if(data === responseUuid) {
resolve(serial); //or resolve(port);
}
});
serial.open(function(err) {
if(err) {
reject('failed to open');
} else {
serial.write(pingUuid + lineEnd);
}
});
})
.finally(function() { // <<<<<<<<<<<
serial.close(); // "dispose".
});
};
Unlike .then, .finally's handler doesn't modify the value/reason, so can be placed in mid-chain without the need to worry about returning a value or rethrowing an error. This point is rather poorly made in the Bluebird documentation.
With disposal in pingPort()
, findPort()
will simplify as follows :
var findPort = function(){
com.listAsync().map(function(port) {
return pingPort(port).timeout(100)
.catch(promise.TimeoutError, function(err) {
console.log("Ping timout: " + port.comName);
})
.catch(function(err){
console.log("Ping error: " + port.comName);
});
});
}();
Going further, you may want to do more with the array of promises returned by .map()
, but that's another issue.
How can I release a resource in a nodeJS Q promise-chain and return a promise?
Instead of done
use finally
, which returns a promise:
https://github.com/kriskowal/q/wiki/API-Reference#promisefinallycallback
module.exports = function fetchRecords() {
return openConnection()
.then(fetchRecords)
.then(groupById)
.catch(rethrow)
.finally(closeConnection);
}
Catch all block in nested promises
What you want is not possible in the way you want it.
The reason is simple: the hierarchy of those .then
s is different.
To make it easier to understand, imagine, that it's all synchronous (it isn't, and shouldn't be; that's just a thought experiment). Without the catch
and finally
, it would look like this:
function callGet(){
try{
// vvvvvvvvvvvv--- for sake of simplicity, assume that it's here (although it isn't)
(function get(...){
axios.get(...)
// (1) then in get block
})(...)
// (4) then in callGet block
}catch(err){
// (5) catch in callGet block if errors in callGet block
}
}
To add the catch
and finally
, you'd have to change it into something like this:
function callGet(){
try{
try{
try{
// vvvvvvvvvvvv--- for sake of simplicity, assume that it's here (although it isn't)
(function get(...){
axios.get(...)
// (1) then in get block
})(...)
// (4) then in callGet block
}catch(err){
// (2) catch in get block if errors in get block
}
}catch(err){
// (5) catch in callGet block if errors in callGet block
}
}finally{
// (3) run after callGet block
}
}
That's all nice, and actually, it would work, but (unfortunately, there is a "but") now just try to move the catch (2)
and the finally (3)
into the get
function. There's clearly no way to do so (without messing up their order)!
With promises, there would be a very hacky and complicated way to do something similar to it, but just let it alone, and let me explain why you shouldn't do that.
Every function should do its own job. Preferably one job. Your get
function has nothing to do with the UI, etc. (at least it shouldn't have). Its job is to make a network request, maybe process the response and handle errors. However, it should only handle its own errors, but not its caller's.
If it were in the way you want, the catch
of get
(2
) would also capture and handle the errors thrown in callGet
(4
), while leaving only the errors thrown from that catch
handler (2
) for callGet
's catch
(5
).
That would make your code complicated, unsemantic, unreliable, and therefore hard to maintain.
The best you can do is to either handle the error by yourself in get
, or to leave the error handling to the caller and don't try to mess up with your caller's job. If you're writing a library, you may offer helper methods for the caller, that composes promises in some unique way, but it's up to the caller to decide whether to use it or not.
If you want to enforce that anyway, you might use a different programming pattern for that (I'm not the good person to ask which), JS is flexible enough to let you implement it.
Best practice for running a background process in Node?
A good code structuring pattern that will ensure proper cleanup is the promise disposer pattern:
async function testMyCoolUI() {
await uiFramework.openMyApp(url);
await sleep(2000);
await withPollingForPopup(async () => {
await uiFramework.clickButtonX();
await uiFramework.clickButtonY();
});
}
async function withPollingForPopup(run) {
try {
startPollingForPopup(); // not waiting for anything
return await run();
} finally {
stopPollingForPopup(); // optionally `await` it
}
}
This assumes a background process, possibly an event subscription, that can be started and stopped.
Alternatively, if the background process does return a promise which rejects on errors and you want to abort as soon as possible, you might use
async function withPollingForPopup(run) {
const poll = runPollingForPopup();
const [res] = await Promise.all([
run().finally(poll.stop),
poll.promise
]);
return res;
}
How to escape from .then() without executing the next statement in Express Js Promise?
One cannot really break out of a then
chain other than by nesting or throwing an exception. Yes, in this case you probably should throw
, but you don't need to .catch
it when you avoid the Promise
constructor antipattern and just make it a chained promise:
exports.company_add = (admin_email, admin_info) => {
return connectionPool.getConnection().then(connection => {
return connection.promise().query('SELECT * FROM admin WHERE admin_email = ?', [admin_email])
.then(([rows, field]) => {
if (rows.length)
throw new Error('Email exist');
else
return connection.promise().query('INSERT INTO companydb.admin SET ?', [admin_info])
})
.then((result) => {
console.log('result')
if (result[0].affectedRows !== 1)
throw new Error('INSERT FAIL');
return result;
})
.finally(() => {
connection.release();
})
}, err => {
console.error(err);
throw new Error('CONNECTION FAIL');
});
};
Javascript Promises how to do post resolution cleanup
You'll want to use the disposer pattern (and avoid the Promise
constructor antipattern):
function with1(callback) {
return async1() // do some async stuff that itself returns a promise :)
.then(function(value) {
// set some global states based on the value for the duration of this chain
const result = Promise.resolve(value) // or whatever
.then(callback);
return result.then(cleanup, cleanup);
function cleanup() {
// clean up the global states regardless of the failure or success of result
return result;
}
});
}
You can use this like
function wrapper() {
with1(() =>
func2().then(func3) // whatever should run in the altered context
);
}
Related Topics
How to Select All Text in Contenteditable Div
Get the Dom Path of the Clicked <A>
Converting Em to Px in JavaScript (And Getting Default Font Size)
Creating a CSS Class in Jquery
Es6 Module Import Giving "Uncaught Syntaxerror: Unexpected Identifier"
How to Limit Options Selected in a HTML Select Box
How to Disable Copy Paste (Browser)
Js: Get Array of All Selected Nodes in Contenteditable Div
Safari: Absolutely Positioned Divs Not Moving When Updated via Dom
Using CSS Modules How to Define More Than One Style Name
How to Set Textarea Scroll Bar to Bottom as a Default
Dynamically Add CSS to Page via JavaScript
Get All Computed Style of an Element
Extract the Current Dom and Print It as a String, with Styles Intact
Setting a Backgroundimage with React Inline Styles