最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to use catch and finally with when.map - Stack Overflow

programmeradmin1浏览0评论

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

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
Share Improve this question asked Apr 15, 2014 at 16:03 t.nieset.niese 40.9k9 gold badges78 silver badges109 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 2

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.
})

Based on the answer of Benjamin Gruenbaum I create a replacement of when.map that uses settle internally, and will trigger the cache and finally when all promises of the map have been handled.

  var settle = {};
  var arrayMap = Array.prototype.map;

  settle.map = function(array, f) {

    var arrayOfPromises = arrayMap.call(array,function(x) {
      return when.resolve(x).then(f);
    });

    return when.settle(arrayOfPromises)
    .then(function(descriptors) {
      var result = [];

      descriptors.forEach(function(descriptor) {
        if( descriptor.state === 'rejected') {
          throw descriptor.reason;
        }

        result.push(descriptor.value);
      });

      return result;
    });
  };

When I now replace when.map with settle.map in my original code, then the output/execution order is as I require it:

 testMapper called with: 1
 testMapper called with: 2
 testMapper called with: 3
 testMapper called with: 4
 reject: 1
 reject: 2
 reject: 3
 reject: 4
 [Error: error: 1]
 finally
发布评论

评论列表(0)

  1. 暂无评论