Подтвердить что ты не робот

Обработка ошибок в Promise.all

У меня есть массив из Promises, который я разрешаю с помощью Promise.all(arrayOfPromises);

Я продолжаю продолжать цепочку обещаний. Выглядит что-то вроде этого

existingPromiseChain = existingPromiseChain.then(function() {
  var arrayOfPromises = state.routes.map(function(route){
    return route.handler.promiseHandler();
  });
  return Promise.all(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
  // do stuff with my array of resolved promises, eventually ending with a res.send();
});

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

Я пробовал делать что-то вроде..

existingPromiseChain = existingPromiseChain.then(function() {
      var arrayOfPromises = state.routes.map(function(route){
        return route.handler.promiseHandler()
          .then(function(data) {
             return data;
          })
          .catch(function(err) {
             return err
          });
      });
      return Promise.all(arrayOfPromises)
    });

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
      // do stuff with my array of resolved promises, eventually ending with a res.send();
});

Но это не решает.

Спасибо!

-

Edit:

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

Node Экспресс-серверная цепочка

serverSidePromiseChain
    .then(function(AppRouter) {
        var arrayOfPromises = state.routes.map(function(route) {
            return route.async();
        });
        Promise.all(arrayOfPromises)
            .catch(function(err) {
                // log that I have an error, return the entire array;
                console.log('A promise failed to resolve', err);
                return arrayOfPromises;
            })
            .then(function(arrayOfPromises) {
                // full array of resolved promises;
            })
    };

Вызов API (вызов route.async)

return async()
    .then(function(result) {
        // dispatch a success
        return result;
    })
    .catch(function(err) {
        // dispatch a failure and throw error
        throw err;
    });

Поместите .catch для Promise.all до того, как. then, похоже, послужил целью уловить любые ошибки из исходного promises, но затем вернул весь массив к следующему. then

Спасибо!

4b9b3361

Ответ 1

Promise.all - это все или ничего. Он решает, как только все promises в массиве разрешат или отклонятся, как только один из них отклоняется. Другими словами, он либо разрешается с массивом всех разрешенных значений, либо отклоняется с одной ошибкой.

В некоторых библиотеках есть что-то, называемое Promise.when, которое, как я понимаю, вместо этого ожидает, что все promises в массиве будут либо разрешаться, либо отклоняться, но я не знаком с ним, и это не в ES6.

Ваш код

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

Единственная причина, по которой я могу думать о том, почему она "не разрешила", состоит в том, что она не работает в коде, который вы не показываете нам, и о причине, по которой вы не видите сообщения об ошибке, заключается в том, что эта цепочка обещаний не прекращается с окончательным уловом (насколько вы показываете нам в любом случае).

Я взял на себя смелость разделить "существующую цепочку" с вашего примера и прекратить цепочку с уловкой. Это может быть неправильным для вас, но для людей, читающих это, важно всегда либо возвращать или прекращать цепочку, либо потенциальные ошибки, даже ошибки кодирования, будут скрыты (что, как я подозреваю, произошло здесь):

Promise.all(state.routes.map(function(route) {
  return route.handler.promiseHandler().catch(function(err) {
    return err;
  });
}))
.then(function(arrayOfValuesOrErrors) {
  // handling of my array containing values and/or errors. 
})
.catch(function(err) {
  console.log(err.message); // some coding error in handling happened
});

Ответ 2

НОВЫЙ ОТВЕТ

const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));

СТАРЫЙ ОТВЕТ

Мы должны написать пользовательский Promise.all(). Вот решение, которое я использую в своем проекте. Ошибка будет возвращена как нормальный результат. После завершения всех обещаний мы можем отфильтровать ошибку.

const Promise_all = promises => {
  return new Promise((resolve, reject) => {
    const results = [];
    let count = 0;
    promises.forEach((promise, idx) => {
      promise
        .catch(err => {
          return err;
        })
        .then(valueOrError => {
          results[idx] = valueOrError;
          count += 1;
          if (count === promises.length) resolve(results);
        });
    });
  });
};

const results = await Promise_all(promises)
const validResults = results.filter(result => !(result instanceof Error));

Ответ 3

Чтобы продолжить цикл Promise.all (даже если Promise отклоняет), я написал функцию утилиты, которая называется executeAllPromises. Эта функция утилиты возвращает объект с results и errors.

Идея состоит в том, что все Promises, которые вы переходите на executeAllPromises, будут завернуты в новое обещание, которое всегда будет разрешено. Новое обещание разрешается с массивом, который имеет 2 точки. Первое пятно содержит разрешающее значение (если оно есть), а второе пятно сохраняет ошибку (если обернутый Обещание отклоняется).

В качестве последнего шага executeAllPromises накапливает все значения завернутого Promises и возвращает конечный объект с массивом для results и массивом для errors.

Вот код:

function executeAllPromises(promises) {
  // Wrap all Promises in a Promise that will always "resolve"
  var resolvingPromises = promises.map(function(promise) {
    return new Promise(function(resolve) {
      var payload = new Array(2);
      promise.then(function(result) {
          payload[0] = result;
        })
        .catch(function(error) {
          payload[1] = error;
        })
        .then(function() {
          /* 
           * The wrapped Promise returns an array:
           * The first position in the array holds the result (if any)
           * The second position in the array holds the error (if any)
           */
          resolve(payload);
        });
    });
  });

  var errors = [];
  var results = [];

  // Execute all wrapped Promises
  return Promise.all(resolvingPromises)
    .then(function(items) {
      items.forEach(function(payload) {
        if (payload[1]) {
          errors.push(payload[1]);
        } else {
          results.push(payload[0]);
        }
      });

      return {
        errors: errors,
        results: results
      };
    });
}

var myPromises = [
  Promise.resolve(1),
  Promise.resolve(2),
  Promise.reject(new Error('3')),
  Promise.resolve(4),
  Promise.reject(new Error('5'))
];

executeAllPromises(myPromises).then(function(items) {
  // Result
  var errors = items.errors.map(function(error) {
    return error.message
  }).join(',');
  var results = items.results.join(',');
  
  console.log(`Executed all ${myPromises.length} Promises:`);
  console.log(`— ${items.results.length} Promises were successful: ${results}`);
  console.log(`— ${items.errors.length} Promises failed: ${errors}`);
});

Ответ 4

Как сказал @jib,

Promise.all это все или ничего.

Тем не менее, вы можете контролировать некоторые обещания, которые "разрешены", и мы хотели бы перейти к .then.

Например.

  Promise.all([
    doMustAsyncTask1,
    doMustAsyncTask2,
    doOptionalAsyncTask
    .catch(err => {
      if( /* err non-critical */) {
        return
      }
      // if critical then fail
      throw err
    })
  ])
  .then(([ mustRes1, mustRes2, optionalRes ]) => {
    // proceed to work with results
  })

Ответ 5

Использование Async await -

здесь одна асинхронная функция func1 возвращает разрешенное значение, а func2 выдает ошибку и возвращает ноль в этой ситуации, мы можем обработать ее так, как мы хотим, и вернуть соответствующим образом.

const callingFunction  = async () => {
    const manyPromises = await Promise.all([func1(), func2()]);
    console.log(manyPromises);
}


const func1 = async () => {
    return 'func1'
}

const func2 = async () => {
    try {
        let x;
        if (!x) throw "x value not present"
    } catch(err) {
       return null
    }
}

callingFunction();

Вывод - ['func1', ноль]

Ответ 6

если вы используете библиотеку q https://github.com/kriskowal/q, у нее есть метод q.allSettled(), который может решить эту проблему, вы можете обработать каждое обещание в зависимости от его состояния, либо выполненное, либо отклоненное, так что

existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
  return route.handler.promiseHandler();
});
return q.allSettled(arrayOfPromises)
});

existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
//so here you have all your promises the fulfilled and the rejected ones
// you can check the state of each promise
arrayResolved.forEach(function(item){
   if(item.state === 'fulfilled'){ // 'rejected' for rejected promises
     //do somthing
   } else {
     // do something else
   }
})
// do stuff with my array of resolved promises, eventually ending with a res.send();
});

Ответ 7

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

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

const manyPromises = Promise.all([func1(), func2()]).then(result => {
    console.log(result[0]);  // undefined
    console.log(result[1]);  // func2
});

function func1() {
    return new Promise( (res, rej) => rej('func1')).catch(err => {
        console.log('error handled', err);
    });
}

function func2() {
    return new Promise( (res, rej) => setTimeout(() => res('func2'), 500) );
}

Ответ 8

Для тех, кто использует ES8, который здесь спотыкается, вы можете сделать что-то вроде следующего, используя асинхронные функции:

var arrayOfPromises = state.routes.map(async function(route){
  try {
    return await route.handler.promiseHandler();
  } catch(e) {
    // Do something to handle the error.
    // Errored promises will return whatever you return here (undefined if you don't return anything).
  }
});

var resolvedPromises = await Promise.all(arrayOfPromises);

Ответ 9

Рассматривали ли вы Promise.prototype.finally()?

Кажется, он предназначен для того, чтобы делать именно то, что вы хотите - выполнить функцию, когда все обещания будут выполнены (разрешены/отклонены), независимо от того, какие обещания были отклонены.

Из документации MDN:

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

Метод finally() очень похож на вызов .then(onFinally, onFinally) однако есть несколько отличий:

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

Обратный вызов finally не получит никаких аргументов, поскольку нет надежных средств определения того, было ли обещание выполнено или отклонено. Этот вариант использования предназначен именно для случаев, когда вас не волнует причина отклонения или ценность выполнения, и поэтому нет необходимости указывать ее.

В отличие от Promise.resolve(2).then(() => {},() => {}) (который будет разрешен с неопределенным значением), Promise.resolve(2).finally(() => {}) будет быть решена с помощью 2. Аналогично, в отличие от Promise.reject(3).then(() => {},() => {}) (который будет выполняться с неопределенным значением), Promise.reject(3).finally(() => {}) будет отклонено с 3.

== Откат ==

Если ваша версия JavaScript не поддерживает Promise.prototype.finally() вы можете использовать этот обходной путь от Джейка Арчибальда: Promise.all(promises.map(p => p.catch(() => undefined)));

Ответ 10

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

function promiseNoReallyAll (promises) {
  return new Promise(
    async (resolve, reject) => {
      const failedPromises = []

      const successfulPromises = await Promise.all(
        promises.map(
          promise => promise.catch(error => {
            failedPromises.push(error)
          })
        )
      )

      if (failedPromises.length) {
        reject(failedPromises)
      } else {
        resolve(successfulPromises)
      }
    }
  )
}

Ответ 11

Вы всегда можете обернуть свое обещание, возвращая функции таким образом, чтобы они улавливали сбой и возвращали вместо него согласованное значение (например, error.message), поэтому исключение не будет полностью свернуто до функции Promise.all и отключит ее.

async function resetCache(ip) {

    try {

        const response = await axios.get('http://${ip}/resetcache');
        return response;

    }catch (e) {

        return {status: 'failure', reason: 'e.message'};
    }

}

Ответ 12

Вот как Promise.all предназначен для работы. Если одно обещание reject(), весь метод сразу завершается неудачей.

В некоторых случаях может потребоваться, чтобы Promise.all обещаний. Чтобы это произошло, просто не используйте операторы reject() в своем обещании. Однако, чтобы ваше приложение/сценарий не зависало в случае, если какое-либо отдельное обещание никогда не получит ответ, вам нужно установить таймаут.

function getThing(uid,branch){
    return new Promise(function (resolve, reject) {
        xhr.get().then(function(res) {
            if (res) {
                resolve(res);
            } 
            else {
                resolve(null);
            }
            setTimeout(function(){reject('timeout')},10000)
        }).catch(function(error) {
            resolve(null);
        });
    });
}

Ответ 13

Я написал библиотеку npm, чтобы справиться с этой проблемой более красиво. https://github.com/wenshin/promiseallend

Установка

npm i --save promiseallend

2017-02-25 новый api, он не нарушает обещание принципов

const promiseAllEnd = require('promiseallend');

const promises = [Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)];
const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)};

// input promises with array
promiseAllEnd(promises, {
    unhandledRejection(error, index) {
        // error is the original error which is 'error'.
        // index is the index of array, it a number.
        console.log(error, index);
    }
})
    // will call, data is `[1, undefined, 2]`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// input promises with object
promiseAllEnd(promisesObj, {
    unhandledRejection(error, prop) {
        // error is the original error.
        // key is the property of object.
        console.log(error, prop);
    }
})
    // will call, data is `{k1: 1, k3: 2}`
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

// the same to `Promise.all`
promiseAllEnd(promises, {requireConfig: true})
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [false, true, false]})
    // won't call
    .then(data => console.log(data))
    // will call, `error.detail` is 'error', `error.key` is number 1.
    .catch(error => console.log(error.detail))

// requireConfig is Array
promiseAllEnd(promises, {requireConfig: [true, false, false]})
    // will call, data is `[1, undefined, 2]`.
    .then(data => console.log(data))
    // won't call
    .catch(error => console.log(error.detail))

--------------------------------

Старый плохой api, не используйте его!

let promiseAllEnd = require('promiseallend');

// input promises with array
promiseAllEnd([Promise.resolve(1), Promise.reject('error'), Promise.resolve(2)])
    .then(data => console.log(data)) // [1, undefined, 2]
    .catch(error => console.log(error.errorsByKey)) // {1: 'error'}

// input promises with object
promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject('error'), k3: Promise.resolve(2)})
    .then(data => console.log(data)) // {k1: 1, k3: 2}
    .catch(error => console.log(error.errorsByKey)) // {k2: 'error'}