Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use catch and finally with when.map

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
like image 921
t.niese Avatar asked Apr 15 '14 16:04

t.niese


1 Answers

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.
})
like image 77
Benjamin Gruenbaum Avatar answered Oct 13 '22 10:10

Benjamin Gruenbaum