Как использовать 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