How to use catch and finally with when.map

javascript, node.js, promise, when-js

Solution

Your best bet is to go with `when.settle`, settle returns all promises when they resolve, not when they fulfill so you can manually check which one did well and which one did not.

var arrayOfPromises = array.map(testMapper);
when.settle(arrayOfPromises).then(function(descriptors){
     descriptors.forEach(function(d){
         if(d.state === "rejected"){
             // do cleanup for that promise, you can access its rejection reason here
             // and do any cleanup you want
         } else{
            // successful results accessed here
            console.log("Successful!", d.value);
         }
     })
});

Note:

This is actually not a small problem. When I say not a small problem, what I mean is that it's a huge problem that's really hard to solve correctly. There are multiple implied behaviors here and edge cases.

Consider reading this somewhat lengthy discussion. If you're willing to consider - Bluebird has an experimental `promise-using` branch that allows specifying disposers, which would let you do this rather easily.

You would be able to do:

using(pool.getConnectionAsync().disposer("close"), function(connection) {
   return connection.queryAsync("SELECT * FROM TABLE");
}).then(function(rows) {
    console.log(rows);
});

Or in the multiple resource case:

var a = Promise.cast(externalPromiseApi.getResource1()).disposer("close");
var b = Promise.cast(externalPromiseApi.getResource2()).disposer("close");
using(a, b, function(resource1, resource2) {
    // once the promise returned here is resolved, we have a deterministic guarantee that 
    // all the resources used here have been closed.
})

Problem

I want to use the `when.map` function to process some data. After the data is processed I need to do some cleanup (e.g. release the currently used database connection back to the connection pool). The problem with my approach using `catch` and `finally` is that `finally` is called when the first `reject` occurs, and while other mappings are still in progress. So how can I wait until all of the mapping promises are finished, so that it is possible to do a save cleanup. ``` require('when/monitor/console'); var when = require('when'); function testMapper(value) { console.log('testMapper called with: '+value); return when.promise(function(resolve, reject) { setTimeout(function() { console.log('reject: '+value); reject(new Error('error: '+value)); },100*value); }); } when.map([1,2,3,4],testMapper) .then(function() { console.log('finished') }) .catch(function(e) { console.log(e); }) .finally(function() { console.log('finally') }); ``` Output ``` testMapper called with: 1 testMapper called with: 2 testMapper called with: 3 testMapper called with: 4 reject: 1 [Error: error: 1] finally reject: 2 [promises] Unhandled rejections: 1 Error: error: 2 at null._onTimeout (index.js:9:14) reject: 3 [promises] Unhandled rejections: 2 Error: error: 2 at null._onTimeout (index.js:9:14) Error: error: 3 at null._onTimeout (index.js:9:14) reject: 4 [promises] Unhandled rejections: 3 Error: error: 2 at null._onTimeout (index.js:9:14) Error: error: 3 at null._onTimeout (index.js:9:14) Error: error: 4 at null._onTimeout (index.js:9:14) ``` Environmentinformation: - whenjs: v3.1.0 - node: v0.10.26

Original source

Related problems