Несколько объектов с отложенной передачей передаются в jQuery.when проблема [duplicate]

Выполните следующие шаги:

  1. Откройте файл catalina.sh из tomcat / bin.
  2. Chnage JAVA_OPTS в
    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"
    
  3. Перезапустите ваш tomcat
241
задан Nathan Hagen 27 June 2016 в 16:10
поделиться

13 ответов

Ответ Беньямина предлагает отличную абстракцию для решения этой проблемы, но я надеялся на менее абстрактное решение. Явный способ разрешить эту проблему - просто вызвать .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.

44
ответ дан Nathan Hagen 19 August 2018 в 14:56
поделиться
  • 1
    @Benjamin Я думаю, что решение Nathan очень простое и идиоматическое для Promise s. Хотя ваш reflect улучшает повторное использование кода, он также устанавливает другой уровень абстракции. Поскольку ответ Натана до сих пор получал лишь небольшую долю голосов по сравнению с вашими, я задаюсь вопросом, является ли это признаком проблемы с его решением, которое я еще не узнал. – ftor 9 July 2016 в 10:54
  • 2
    @ LUH3417 это решение концептуально менее звуковое, поскольку оно обрабатывает ошибки как значения и не отделяет ошибки от ошибок. Например, если одно из обещаний разрешает законно значение, которое может быть выброшено (что вполне возможно), это сильно нарушается. – Benjamin Gruenbaum 2 August 2016 в 14:25
  • 3
    @BenjaminGruenbaum Так, например, new 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:53
  • 4
    Причина, по которой это плохая идея, заключается в том, что она связывает реализацию Promise с конкретным вариантом использования, когда-либо используемым только в конкретном варианте Promise.all(), а затем потребителю Promise также становится известно, что конкретное обещание не будет отвергнуть, но проглотит его ошибки. На самом деле метод reflect() можно было бы сделать менее абстрактным и более явным, называя его PromiseEvery(promises).then(...). Сложность ответа выше, чем у Benjamin's, должна много говорить об этом решении. – Neil 28 September 2016 в 21:51

Вы можете выполнить свою логику последовательно через синхронный исполнитель 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>

0
ответ дан amaksr 19 August 2018 в 14:56
поделиться

Конечно, вам просто нужен 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");
});
193
ответ дан Benjamin Gruenbaum 19 August 2018 в 14:56
поделиться
  • 1
    Я думаю, что это отличное решение. Можете ли вы изменить его, чтобы включить более простой синтаксис? Суть проблемы заключается в том, что если вы хотите обрабатывать ошибки в суб-обещаниях, вы должны поймать их и вернуть ошибку. Так, например: gist.github.com/nhagen/a1d36b39977822c224b8 – Nathan Hagen 15 July 2015 в 17:20
  • 2
    Упрощенный синтаксис? Я не понимаю, что вы имеете в виду. – Benjamin Gruenbaum 15 July 2015 в 17:32
  • 3
    @NathanHagen позволяет вам определить, что отклонено, а что выполнено и извлекает проблему для оператора многократного использования. – Benjamin Gruenbaum 15 July 2015 в 19:14
  • 4
    В ответ на мою собственную проблему я создал следующий пакет npm: github.com/Bucabug/promise-reflect npmjs.com/package/promise-reflect – SamF 11 October 2016 в 07:44
  • 5
    Я столкнулся с этим вопросом некоторое время назад, и я создал для него этот пакет npm: npmjs.com/package/promise-all-soft-fail – velocity_distance 28 November 2016 в 23:40

Мне очень нравится ответ Бенджамина и то, как он в основном превращает все обещания в постоянно разрешаемые, но иногда с ошибками. :) Вот моя попытка вашего запроса на случай, если вы ищете альтернативы. Этот метод просто рассматривает ошибки как допустимые результаты и кодируется аналогично 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);
  });
}
7
ответ дан Bergi 19 August 2018 в 14:56
поделиться
  • 1
    Обычно это называется settle. У нас это тоже в bluebird, мне нравится лучше отражать, но это жизнеспособное решение, когда у вас есть это для массива. – Benjamin Gruenbaum 15 July 2015 в 08:31
  • 2
    Хорошо, sett будет лучшим именем. :) – Kuba Wyrostek 15 July 2015 в 08:34
  • 3
    Это очень похоже на явное обещание строительства антипаттерн. Следует отметить, что вы никогда не должны писать такую ​​функцию самостоятельно, но используйте тот, который поставляется вашей библиотекой (нормально, родной ES6 немного скуден). – Bergi 16 July 2015 в 19:25
  • 4
    Не могли бы вы правильно использовать конструктор Promise (и избегать этого var resolve thingy)? – Bergi 16 July 2015 в 19:26
  • 5
    Берги, не стесняйтесь изменять ответ, но считаете необходимым. – Kuba Wyrostek 16 July 2015 в 19:33

Подобный ответ, но более вероятно идиоматический для 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:") и т. д.)

145
ответ дан Dan Dascalescu 19 August 2018 в 14:56
поделиться
  • 1
    @jib спас мой день. Лучший подход. – kushdilip 25 November 2016 в 07:39
  • 2
    @KarlBateman Я думаю, вы в замешательстве. Функции ордера разрешают или отклоняют в этом неважно, так как часть .map(p => p.catch(e => e)) превращает все отклонения в разрешенные значения, поэтому Promise.all все еще ждет, чтобы все закончилось, разрешали ли отдельные функции или отклонялись, независимо от того, сколько времени они занимают. Попробуй. – jib 21 December 2016 в 16:44
  • 3
    .catch(e => console.log(e)); никогда не вызывается, потому что это никогда не прерывается – bfred.it 2 March 2017 в 12:31
  • 4
    @ bfred.it Это правильно. Хотя завершающие цепочки обещаний с catch обычно являются хорошей практикой IMHO . – jib 2 March 2017 в 16:11
  • 5
    @SuhailGupta Уловит ошибку 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?

0
ответ дан Edward 19 August 2018 в 14:56
поделиться

Я бы сделал:

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
-1
ответ дан FRocha 19 August 2018 в 14:56
поделиться

Мой подход:

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

У него есть свои недостатки, но в целом он может быть эффективным. Я склонен использовать этот шаблон, когда меня не волнуют причины ошибок.

-1
ответ дан Konstantin Cherkasov 19 August 2018 в 14:56
поделиться
  • 1
    Какая польза от этого дает ответы от 3 лет назад? – Jared Smith 7 June 2018 в 14:17
var err;
Promise.all([
    promiseOne().catch(function(error) { err = error;}),
    promiseTwo().catch(function(error) { err = error;})
]).then(function() {
    if (err) {
        throw err;
    }
});

Promise.all проглотит любое отклоненное обещание и сохранит ошибку в переменной, поэтому она вернется, когда все обещания будут решены. Затем вы можете повторно выбросить ошибку или сделать что угодно. Таким образом, я думаю, вы выберете последнее отклонение вместо первого.

3
ответ дан martin770 19 August 2018 в 14:56
поделиться
  • 1
    Похоже, что это может агрегировать ошибки, создавая массив и используя 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,
        }))));
    };
}
1
ответ дан mpen 19 August 2018 в 14:56
поделиться

Я не знаю, какую библиотеку обещаний вы используете, но у большинства есть что-то вроде allSettled .

Edit: Ok, так как вы хотите использовать простой ES6 без внешних библиотек, нет такого метода.

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

-3
ответ дан Sebastian S 19 August 2018 в 14:56
поделиться
  • 1
    Я отредактировал свой вопрос, чтобы уточнить. Поскольку ES6 поставляется с обещаниями, я бы хотел избежать использования другой библиотеки для того, что, по моему мнению, является базовой функциональностью. Я думаю, что хорошим местом для получения ответа было бы скопировать источник из одной из библиотек обещаний. – Nathan Hagen 15 July 2015 в 08:05

У меня была та же проблема, и я решил ее следующим образом:

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, когда оно разрешено после таймаута.

4
ответ дан user1016265 19 August 2018 в 14:56
поделиться
  • 1
    Но это вызывает все обещания по желанию, когда вы запускаете Promise.all. Я ищу способ послушать, когда все обещания были вызваны, но не вызывайте их сами. Благодарю. – SudoPlz 3 January 2018 в 13:25
  • 2
    @SudoPlz метод all() делает это, он ждет выполнения всех обещаний или отклонения хотя бы одного из них. – user1016265 3 January 2018 в 21:46
  • 3
    это правда, но он не просто ждет, он фактически вызывает / запускает / запускает процесс. Если вы хотели бы выполнить обещания где-то еще, что было бы невозможно, потому что .all все расстреливает. – SudoPlz 3 January 2018 в 22:10
  • 4
    @SudoPlz надеется, что это изменит ваше мнение jsfiddle.net/d1z1vey5 – user1016265 4 January 2018 в 22:35
  • 5
    Я стою исправлено. До сих пор я думал, что обещания запускаются только тогда, когда кто-то вызывает их (a.k.a then или вызов .all), но они запускаются при создании. – SudoPlz 5 January 2018 в 01:23
-1
ответ дан user2273990 31 October 2018 в 02:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: