Как использовать catch и, наконец, когда..map

Я хочу использовать when.map функция для обработки некоторых данных. После обработки данных мне нужно выполнить некоторую очистку (например, освободить текущее используемое соединение с базой данных обратно в пул соединений).

Проблема с моим подходом к использованию catch а также finally в том, что finally называется, когда первый reject происходит, и в то время как другие сопоставления все еще продолжаются.

Итак, как мне дождаться завершения всех обещаний сопоставления, чтобы можно было выполнить очистку сохранения.

  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')
  });

Выход

 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:

  • когда: v3.1.0
  • узел: v0.10.26

2 ответа

Решение

Лучше всего пойти с when.settle, build возвращает все обещания, когда они выполняются, а не когда они выполняются, так что вы можете вручную проверить, какое из них сработало, а какое - нет.

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

Замечания:

На самом деле это не маленькая проблема. Когда я говорю не маленькая проблема, я имею в виду, что это огромная проблема, которую действительно трудно решить правильно. Здесь есть несколько предполагаемых вариантов поведения и крайние случаи.

Подумайте о том, чтобы прочитать это несколько длинное обсуждение Если вы готовы рассмотреть - у Bluebird есть экспериментальная promise-using ветка, которая позволяет указывать диспозиторов, что позволит вам сделать это довольно легко.

Вы сможете сделать:

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

Или в случае нескольких ресурсов:

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

Основываясь на ответе Бенджамина Грюнбаума, я создаю замену when.map который использует внутреннее урегулирование, и вызовет cache а также finally когда все обещания map были обработаны.

  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.map с settle.map в моем исходном коде порядок вывода / выполнения такой, какой мне требуется:

 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
Другие вопросы по тегам