javascriptpromisedisposebluebird

What is the promise disposer pattern?


I've read about the promise disposer pattern in several places but I can't figure out what it is. It was suggested to me to use it in code that looks like:

function getDb(){
    return myDbDriver.getConnection();
}

var users = getDb().then(function(conn){
     return conn.query("SELECT name FROM users").finally(function(users){
         conn.release();
     });
});

What's the promise disposer pattern and how does it apply here?


Note - in native promises, I shim .finally as "add both rejection and fulfillment handlers that return the value but perform an action". I'm using bluebird in this case if it matters.


Solution

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