How to dispose resources in rejected promises with

2019-09-04 15:56发布

问题:

How can I dispose the serial when the promise is rejected?

The dispose function prints error for rejected promises.

Unhandled rejection TypeError: Cannot call method 'isOpen' of undefined

var pingPort = function(port){
    return new promise(function(resolve, reject){
        var serial = new com.SerialPort(port.comName, {
            baudrate: 19200,
            parser: com.parsers.readline(lineEnd)
        }, false);
        serial.on("data", function(data){
            if (data === responseUuid){
                resolve(serial);
            }
        });
        serial.open(function(err){
            if (err){
                //reject(serial)
            }
            else{
                serial.write(pingUuid + lineEnd);
            }
        });
    });
};

var dispose = function(port){
    console.log(port.isOpen());
};

var findPort = function(){
    com.listAsync().map(function(port){
        return pingPort(port).timeout(100).catch(promise.TimeoutError, function(err) {
            console.log("Ping timout: " + port.comName);
            dispose(port);
        }).catch(function(err){
            console.log("Ping error: " + port.comName);
            dispose(port);
        });
    }).each(dispose);
}();

回答1:

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.