What Is the Promise Disposer Pattern

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 .thens 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



Leave a reply



Submit