Выполните следующие шаги:
JAVA_OPTS="-Djava.awt.headless=true -Dfile.encoding=UTF-8 -server -Xms1536m
-Xmx1536m -XX:NewSize=256m -XX:MaxNewSize=256m -XX:PermSize=256m
-XX:MaxPermSize=256m -XX:+DisableExplicitGC"
Ответ Беньямина предлагает отличную абстракцию для решения этой проблемы, но я надеялся на менее абстрактное решение. Явный способ разрешить эту проблему - просто вызвать .catch
во внутренних обещаниях и вернуть ошибку из своего обратного вызова.
let a = new Promise((res, rej) => res('Resolved!')),
b = new Promise((res, rej) => rej('Rejected!')),
c = a.catch(e => { console.log('"a" failed.'); return e; }),
d = b.catch(e => { console.log('"b" failed.'); return e; });
Promise.all([c, d])
.then((result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Promise.all([a.catch(e => e), b.catch(e => e)])
.then(result => console.log('Then', result)) // Then ["Resolved!", "Rejected!"]
.catch(err => console.log('Catch', err));
Сделав этот шаг дальше, вы можете написать общий обработчик улов, который выглядит следующим образом:
const catchHandler = error => ({ payload: error, resolved: false });
, тогда вы можете сделать
> Promise.all([a, b].map(promise => promise.catch(catchHandler))
.then(results => console.log(results))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!', { payload: Promise, resolved: false } ]
. Проблема заключается в том, что пойманные значения будут иметь другой интерфейс, но вы можете сделать это:
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
Затем, чтобы сохранить его, вы получите к ответу Вениамина:
const reflect = promise => promise
.then(successHandler)
.catch(catchHander)
, где он теперь выглядит как
> Promise.all([a, b].map(result => result.then(successHandler).catch(catchHandler))
.then(results => console.log(results.filter(result => result.resolved))
.catch(() => console.log('Promise.all failed'))
< [ 'Resolved!' ]
. Преимущества второго решения - это его абстракция и СУХИЕ. Недостатком является то, что у вас больше кода, и вы должны помнить, что должны отражать все ваши обещания, чтобы сделать их согласованными.
Я бы охарактеризовал свое решение как явное и KISS, но действительно менее надежное. Интерфейс не гарантирует, что вы точно знаете, было ли обещание успешным или неудачным.
Например, у вас может быть следующее:
const a = Promise.resolve(new Error('Not beaking, just bad'));
const b = Promise.reject(new Error('This actually didnt work'));
Этого не поймает a.catch
, поэтому
> Promise.all([a, b].map(promise => promise.catch(e => e))
.then(results => console.log(results))
< [ Error, Error ]
Невозможно сказать, какой из них был фатальным, а какой - нет. Если это важно, тогда вам нужно будет обеспечить соблюдение и интерфейс, который отслеживает, было ли это успешным или нет (что делает reflect
).
Если вы просто хотите обработать ошибки изящно, тогда вы можете просто обрабатывать ошибки как неопределенные значения:
> Promise.all([a.catch(() => undefined), b.catch(() => undefined)])
.then((results) => console.log('Known values: ', results.filter(x => typeof x !== 'undefined')))
< [ 'Resolved!' ]
В моем случае мне не нужно знать ошибку или как она потерпела неудачу - мне просто важно, есть ли у меня значение или нет. Я позволю функции, которая генерирует обещание, беспокоится о регистрации конкретной ошибки.
const apiMethod = () => fetch()
.catch(error => {
console.log(error.message);
throw error;
});
Таким образом, остальная часть приложения может игнорировать свою ошибку, если она захочет, и рассматривать ее как неопределенное значение если он хочет.
Я хочу, чтобы мои функции высокого уровня терпели неудачу и не беспокоились о деталях, почему его зависимости не удались, и я также предпочитаю KISS DRY, когда мне нужно сделать этот компромисс - который в конце концов, почему я решил не использовать reflect
.
Вы можете выполнить свою логику последовательно через синхронный исполнитель nsynjs . Он будет останавливаться на каждом обещании, ждать разрешения / отклонения и либо присваивать результат решения свойству data
, либо выдавать исключение (для обработки вам понадобится блок try / catch). Вот пример:
function synchronousCode() {
function myFetch(url) {
try {
return window.fetch(url).data;
}
catch (e) {
return {status: 'failed:'+e};
};
};
var arr=[
myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js"),
myFetch("https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js"),
myFetch("https://ajax.NONEXISTANT123.com/ajax/libs/jquery/2.0.0/NONEXISTANT.js")
];
console.log('array is ready:',arr[0].status,arr[1].status,arr[2].status);
};
nsynjs.run(synchronousCode,{},function(){
console.log('done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>
Конечно, вам просто нужен reflect
:
const reflect = p => p.then(v => ({v, status: "fulfilled" }),
e => ({e, status: "rejected" }));
reflect(promise).then((v => {
console.log(v.status);
});
Или с ES5:
function reflect(promise){
return promise.then(function(v){ return {v:v, status: "resolved" }},
function(e){ return {e:e, status: "rejected" }});
}
reflect(promise).then(function(v){
console.log(v.status);
});
Или в вашем примере:
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr.map(reflect)).then(function(results){
var success = results.filter(x => x.status === "resolved");
});
Мне очень нравится ответ Бенджамина и то, как он в основном превращает все обещания в постоянно разрешаемые, но иногда с ошибками. :) Вот моя попытка вашего запроса на случай, если вы ищете альтернативы. Этот метод просто рассматривает ошибки как допустимые результаты и кодируется аналогично Promise.all
в противном случае:
Promise.settle = function(promises) {
var results = [];
var done = promises.length;
return new Promise(function(resolve) {
function tryResolve(i, v) {
results[i] = v;
done = done - 1;
if (done == 0)
resolve(results);
}
for (var i=0; i<promises.length; i++)
promises[i].then(tryResolve.bind(null, i), tryResolve.bind(null, i));
if (done == 0)
resolve(results);
});
}
settle
. У нас это тоже в bluebird, мне нравится лучше отражать, но это жизнеспособное решение, когда у вас есть это для массива.
– Benjamin Gruenbaum
15 July 2015 в 08:31
Promise
(и избегать этого var resolve
thingy)?
– Bergi
16 July 2015 в 19:26
Подобный ответ, но более вероятно идиоматический для ES6:
const a = Promise.resolve(1);
const b = Promise.reject(new Error(2));
const c = Promise.resolve(3);
Promise.all([a, b, c].map(p => p.catch(e => e)))
.then(results => console.log(results)) // 1,Error: 2,3
.catch(e => console.log(e));
const console = { log: msg => div.innerHTML += msg + "<br>"};
<div id="div"></div>
В зависимости от типа (ов) возвращаемых значений , ошибки часто можно различить достаточно легко (например, используйте undefined
для «не заботьтесь», typeof
для простых значений без объекта, result.message
, result.toString().startsWith("Error:")
и т. д.)
.map(p => p.catch(e => e))
превращает все отклонения в разрешенные значения, поэтому Promise.all
все еще ждет, чтобы все закончилось, разрешали ли отдельные функции или отклонялись, независимо от того, сколько времени они занимают. Попробуй.
– jib
21 December 2016 в 16:44
.catch(e => console.log(e));
никогда не вызывается, потому что это никогда не прерывается
– bfred.it
2 March 2017 в 12:31
catch
обычно являются хорошей практикой IMHO .
– jib
2 March 2017 в 16:11
e
и возвращает ее как регулярное (успешное) значение. То же, что и p.catch(function(e) { return e; })
, только короче. return
неявно.
– jib
21 March 2017 в 05:54
Я просто написал пользовательскую функцию promise.all()
и использовал ее вместо стандартного Promise.all()
. Если все обещания разрешены, он выполняет точно так же, как и стандартный. Если одно из других обещаний отклонено, оно возвращает первое, которое отклонено так же, как и стандартное, но в отличие от него ждет всех обещаний разрешить / отклонить:
var promise = {}; // custom namespace for promise-related stuff
promise.all = function(values)
{
var e;
return Promise.all(values.map(function(value) {
return Promise.resolve(value).catch(function(error) {
e = e || error;
});
})).then(function(values2) {
if(e)
throw e;
return values2;
});
};
Мы можем пойти еще дальше, чтобы нежно расширьте стандарт Promise.all()
одним дополнительным параметром wait
следующим образом:
(function() {
var stdAll = Promise.all;
Promise.all = function(values, wait) {
if(!wait)
return stdAll.call(Promise, values);
return promise.all(values);
}
})();
Это может быть разумным, так как новая версия обратно совместима со стандартной, просто расширяя ее функциональность. (Мы можем полностью избежать пользовательского пространства имен promise
- он используется для краткости, чтобы показать эту идею.)
Люди, которые разрабатывают стандарты - почему бы не включить это в новый стандарт Promise?
Я бы сделал:
var err = [fetch('index.html').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); }),
fetch('http://does-not-exist').then((success) => { return Promise.resolve(success); }).catch((e) => { return Promise.resolve(e); })];
Promise.all(err)
.then(function (res) { console.log('success', res) })
.catch(function (err) { console.log('error', err) }) //never executed
Мой подход:
const promise1 = Promise.resolve(1)
const promise2 = Promise.reject(2)
const errors = []
Promise.all([
promise1,
promise2
].map((promise) => promise.catch((error) => {
errors.push(error)
}))).then(([response1, response2]) => {
console.log(response1)
// 1
console.log(response2)
// undefined
console.log(errors)
// [2]
})
У него есть свои недостатки, но в целом он может быть эффективным. Я склонен использовать этот шаблон, когда меня не волнуют причины ошибок.
var err;
Promise.all([
promiseOne().catch(function(error) { err = error;}),
promiseTwo().catch(function(error) { err = error;})
]).then(function() {
if (err) {
throw err;
}
});
Promise.all
проглотит любое отклоненное обещание и сохранит ошибку в переменной, поэтому она вернется, когда все обещания будут решены. Затем вы можете повторно выбросить ошибку или сделать что угодно. Таким образом, я думаю, вы выберете последнее отклонение вместо первого.
err.push(error)
, поэтому все ошибки могут быть пузырьками.
– ps2goat
6 April 2018 в 15:03
Это должно соответствовать , как Q это :
if(!Promise.allSettled) {
Promise.allSettled = function (promises) {
return Promise.all(promises.map(p => Promise.resolve(p).then(v => ({
state: 'fulfilled',
value: v,
}), r => ({
state: 'rejected',
reason: r,
}))));
};
}
Я не знаю, какую библиотеку обещаний вы используете, но у большинства есть что-то вроде allSettled .
Edit: Ok, так как вы хотите использовать простой ES6 без внешних библиотек, нет такого метода.
Другими словами: вы должны вручную выполнить свои обещания и разрешить новый комбинированный обещание, как только будут выполнены все обещания.
У меня была та же проблема, и я решил ее следующим образом:
const fetch = (url) => {
return node-fetch(url)
.then(result => result.json())
.catch((e) => {
return new Promise((resolve) => setTimeout(() => resolve(fetch(url)), timeout));
});
};
tasks = [fetch(url1), fetch(url2) ....];
Promise.all(tasks).then(......)
В этом случае Promise.all
будет ожидать, что каждое обещание войдет в состояние resolved
или rejected
.
И имея это решение, мы «останавливаем catch
исполнение» неблокирующим способом. Фактически, мы ничего не останавливаем, мы просто возвращаем Promise
в состояние ожидания, которое возвращает другой Promise
, когда оно разрешено после таймаута.
Promise.all
. Я ищу способ послушать, когда все обещания были вызваны, но не вызывайте их сами. Благодарю.
– SudoPlz
3 January 2018 в 13:25
all()
делает это, он ждет выполнения всех обещаний или отклонения хотя бы одного из них.
– user1016265
3 January 2018 в 21:46
.all
все расстреливает.
– SudoPlz
3 January 2018 в 22:10
then
или вызов .all
), но они запускаются при создании.
– SudoPlz
5 January 2018 в 01:23
Promise
s. Хотя вашreflect
улучшает повторное использование кода, он также устанавливает другой уровень абстракции. Поскольку ответ Натана до сих пор получал лишь небольшую долю голосов по сравнению с вашими, я задаюсь вопросом, является ли это признаком проблемы с его решением, которое я еще не узнал. – ftor 9 July 2016 в 10:54new Promise((res, rej) => res(new Error('Legitimate error'))
не будет отличаться отnew Promise(((res, rej) => rej(new Error('Illegitimate error'))
? Или, кроме того, вы не сможете фильтроватьx.status
? Я добавлю этот момент к моему ответу, так что разница более понятна – Nathan Hagen 2 August 2016 в 15:53Promise.all()
, а затем потребителю Promise также становится известно, что конкретное обещание не будет отвергнуть, но проглотит его ошибки. На самом деле методreflect()
можно было бы сделать менее абстрактным и более явным, называя егоPromiseEvery(promises).then(...)
. Сложность ответа выше, чем у Benjamin's, должна много говорить об этом решении. – Neil 28 September 2016 в 21:51