Promise.allでのエラーの処理


266

私は解決している約束の配列を持っています 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最初に見つかったエラーが返され(残りは無視されます)、残りのプロミスからデータを取得できません配列(エラーにはなりませんでした)。

私は..のようなことをやってみました

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

しかし、それは解決しません。

ありがとう!

-

編集:

以下の答えが完全に真実であるということは、コードは他の理由で壊れていました。誰かが興味を持っている場合に備えて、これが私が最終的に解決したソリューションです...

ノードエクスプレスサーバーチェーン

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

.catchforをPromise.all前に置く.thenことは、元のpromiseからのエラーをキャッチする目的で機能したようですが、配列全体を次の場所に返します.then

ありがとう!


2
あなたの試みはうまくいくようです...多分後でどこかに別の問題があるのでしょうか?
Ry-

.then(function(data) { return data; })完全に省略可能
ベルギ

上記が解決しない唯一の理由は、thenまたはcatchハンドラーのすべてのコードが表示されておらず、内部でエラーがスローされている場合です。ちなみにこのノードは?

1
「既存のチェーン」には最終的な問題がないため、「解決しない」理由を説明するエラーが表示されない場合があります。これを追加して、どのようなエラーが発生するかを確認してください。
ジブ2015年

ここでの答えは次のとおりです。stackoverflow.com/questions/31424561/...
Humoyunアフマド

回答:


189

Promise.all全か無かです。これは、配列の解決のすべての約束いったん解決し、またはできるだけ早くとして拒否1それらのは拒否します。つまり、すべての解決された値の配列で解決するか、単一のエラーで拒否します。

一部のライブラリにはと呼ばれるものがありますPromise.when。これは、配列内のすべての promiseが解決または拒否されるのを待つことになると理解していますが、これについては詳しくありません。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
});

4
あなた(と上記のコメント)は正しかった。私のroute.handler.promiseHandlerは.catch()を実行してエラーを返す必要がありました。最後の.catch()をチェーンの最後に追加する必要もありました。チェーンのすべてのステップで成功/エラーハンドラーを持つことの重要性を伝えてくれてありがとう:)
2015年

2
また、route.handler.promiseHandlerの.catch()でエラーをスローすると、自動的に最後のキャッチに移動することもわかりました。代わりにエラーを返すと、私がやりたいことを実行して配列全体を処理します。
2015年

2
現在、Promise.allSettled()まともなサポートの標準的な方法があります。リファレンスを参照してください
アンドレアMaugars

はい、Promise.all最初のスレッドが失敗すると失敗します。しかし、残念ながら、他のすべてのスレッドは終了するまで実行を続けます。何もキャンセルされず、さらに悪いことに、ではスレッドをキャンセルする方法はありませんPromise。したがって、スレッドが実行している(そして操作している)スレッドが継続する場合は、状態と変数を変更し、CPUを使用しますが、最終的には結果を返しません。混乱を引き起こさないように、これを認識しておく必要があります。たとえば、通話を繰り返す/再試行する場合などです。
MarcWäckerlin

144

新しい答え

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

フューチャープロミスAPI


11
である必要はありeませんがError。たとえば、誰かがのようにそれを返す場合、それは文字列である場合がありますPromise.reject('Service not available')
Klesun

@ArturKlesunでは、どの約束がエラーになったか、どれが失敗しなかったかをどのように分類できますか?
Shubham Jain

5
@ shubham-jain .then()および.catch()Promise.resolve()前者に値を渡しますがPromise.reject()、後者には渡します。たとえば、次のようにオブジェクトでラップできますp.then(v => ({success: true, value: v})).catch(e => ({success: false, error: e}))
Klesun

2
結果をフィルタリングするのはなぜですか?結果に対して何かを実行している場合、これは意味がありません。どの戻り値がどのプロミスからのものかを知るための順序が必要です!
ライアンテイラー

21

Promise.allループを続行するために(Promiseが拒否された場合でも)、というユーティリティ関数を作成しましたexecuteAllPromises。このユーティリティ関数は、resultsおよびでオブジェクトを返しますerrors

アイデアは、渡したすべてのexecuteAllPromisesPromiseが常に解決される新しいPromiseにラップされるというものです。新しいPromiseは、2つのスポットを持つ配列で解決します。最初のスポットは解決値(ある場合)を保持し、2番目のスポットはエラーを保持します(ラップされたPromiseが拒否した場合)。

最終ステップとして、executeAllPromisesラップされた約束のすべての値を蓄積し、の配列を有する最終オブジェクトを返す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}`);
});


2
これはより簡単に行うことができます。stackoverflow.com/a/36115549/918910を
ジブ

18

ES2020では、Promiseタイプに新しいメソッドが導入されています。Promise.allSettledは、すべての入力 Promise が解決されたときにシグナルを提供しますつまり、Promise Promise.allSettled()
が満たされているか、拒否されています。これは、プロミスの状態を気にせず、作業が成功したかどうかに関係なく、作業がいつ完了したかを知りたい場合に便利です。

const promises = [
  fetch('/api-call-1'),
  fetch('/api-call-2'),
  fetch('/api-call-3'),
];
// Imagine some of these requests fail, and some succeed.

const result = await Promise.allSettled(promises);
console.log(result.map(x=>s.status));
// ['fulfilled', 'fulfilled', 'rejected']

詳しくは、v8ブログの投稿https://v8.dev/features/promise-combinatorsをご覧ください。


13

@jibが言ったように、

Promise.all 全か無かです。

ただし、「許可」されて失敗する可能性のある特定のPromiseを制御することはできます.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
  })

6

qライブラリhttps://github.com/kriskowal/qを使用する場合、 q.allSettled()メソッドがあり、この問題を解決できます。フルファイルまたは拒否の状態に応じて、すべてのpromiseを処理できます。

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

いくつかのライブラリ(q)の使用を提案しているので、質問に関連する使用例を提供した方が便利です。現状では、あなたの答えは、このライブラリが問題の解決にどのように役立つかを説明していません。
ishmaelMakitla 2016年

提案された例を追加
Mohamed Mahmoud

1
2018年頃、シンドレが利用できるものを常に確認する必要があります:-)。 github.com/sindresorhus/p-settle。Sindreの単一目的モジュールを使用すると、qのような巨大なライブラリを1ビットだけインポートする必要はありません。
DKebler、

6

非同期待機の使用-

ここでは、1つの非同期関数func1が解決された値を返し、func2がエラーをスローしてこの状況でnullを返しています。これを処理して、必要に応じて返すことができます。

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'、null]


4

ここでつまずく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);

3

個々のpromiseレベルで拒否を処理できるため、結果配列で結果を取得すると、拒否された配列のインデックスはになりますundefined。必要に応じてその状況を処理し、残りの結果を使用できます。

ここでは、最初のプロミスを拒否したため、未定義になりますが、インデックス1にある2番目のプロミスの結果を使用できます。

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


非同期待機を使用している場合、どうすれば同様のことができますか?
Rudresh Ajgaonkar

質問に回答しました。回答のリンクを見つけてください。stackoverflow.com/a/55216763/4079716
Nayan Patel

2

検討しましたPromise.prototype.finally()か?

それはあなたが望むことを正確に行うように設計されているようです-拒否された約束のいくつかに関係なく、すべての約束が解決(解決/拒否)されたら関数を実行します。

MDNのドキュメントから:

このfinally()方法は、Promiseが確定した後、その結果に関係なく、何らかの処理またはクリーンアップを実行する場合に役立ちます。

finally()この方法は、呼び出し元と非常によく似ています.then(onFinally, onFinally)が違いがいくつかあります:

関数をインラインで作成する場合、関数を2回宣言したり、変数を作成したりする代わりに、関数を1回渡すことができます。

約束が履行されたか拒否されたかを判断する信頼できる手段がないため、finallyコールバックは引数を受け取りません。このユースケースは、拒否の理由やフルフィルメントの値を気にしない場合に使用するため、提供する必要はありません。

違ってPromise.resolve(2).then(() => {}, () => {})(未定義で解決される)、Promise.resolve(2).finally(() => {})2で解決されるであろう。同様に、異なりPromise.reject(3).then(() => {}, () => {})(未定義で成就される)、Promise.reject(3).finally(() => {})3で拒否されます。

==フォールバック==

JavaScriptのバージョンがサポートしていないPromise.prototype.finally()場合は、Jake Archibaldの次の回避策を使用できます。Promise.all(promises.map(p => p.catch(() => undefined)));


1
はい、Promises.allSettled()実際に実装されるまで(MDNによってここに文書化されています)、Promises.all.finally()同じことを達成しているように見えます。試してみるところです...
ジェームズ

@jamessこのコメントを正しい答えにしてみませんか?ES6に関する回答はありませんallSettled()
プラビン

@pravin-私が言うことができることから、allSettled()(まだ)どこにも実装されていないので、私は現実を先取りしたくありません。私はで成功しましたPromises.all(myPromiseArray).finally()、そしてそれはこの答えに合います。いったんallSettled()実際に存在したら、私はそれをテストして、それが実際にどのように機能するかを見つけるかもしれません。それまでは、ブラウザが実際に何を実装するか誰が知っていますか?反対の最新情報がない限り...
ジェームズ

そのまだドラフト段階でその真のは@jamess ..しかし、最新のFFとクロムは完全にそれをサポートするように見える...わからない、それの安定性...カントーMozillaのドキュメントとにかく、私はメイクにしようとしていたポイントは、それが見つけることがはるかに簡単だろうということでしたコメントではなく回答だった場合.. ur call it :)
pravin

@pravin-私がコメントを投稿した時点では、どこにも実装されていませんでした。FirefoxとChromeでテストしたところ、FirefoxにPromise.allSettledは実装されていませんが、Chromeには存在するようです。ドキュメントがそれが実装されていると言っているからといって、それが実際に実装されているとは限りません。すぐには使いません。
ジェームズ

0

代わりに、1つの失敗があったときに解決されたプロミスの値を特に気にしないが、それでも実行したい場合は、次のようにして、通常どおりプロミスで解決することができます。それらのすべてが成功し、失敗した約束は拒否されます。

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

0

失敗をキャッチし、代わりに合意された値(たとえば、error.message)を返すように、Promiseの戻り関数を常にラップできます。これにより、例外が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'};
    }

}

0

同期せずにこれを行う方法(回避策)を見つけました。

それで、前に述べたようにPromise.all、すべてではありません。

そう...囲みの約束を使って、キャッチして強制的に解決します。


      let safePromises = originalPrmises.map((imageObject) => {
            return new Promise((resolve) => {
              // Do something error friendly
              promise.then(_res => resolve(res)).catch(_err => resolve(err))
            })
        })
    })

    // safe
    return Promise.all(safePromises)

0

結果のエラーを特定する方法を知る必要があります。標準の予期されるエラーがない場合は、catchブロックの各エラーに対して変換を実行して、結果でそれを識別可能にすることをお勧めします。

try {
  let resArray = await Promise.all(
    state.routes.map(route => route.handler.promiseHandler().catch(e => e))
  );

  // in catch(e => e) you can transform your error to a type or object
  // that makes it easier for you to identify whats an error in resArray
  // e.g. if you expect your err objects to have e.type, you can filter
  // all errors in the array eg
  // let errResponse = resArray.filter(d => d && d.type === '<expected type>')
  // let notNullResponse = resArray.filter(d => d)

  } catch (err) {
    // code related errors
  }

0

エラーログに最適な方法ではありませんが、いつでもすべてをpromiseAllの配列に設定し、結果の結果を新しい変数に格納できます。

graphQLを使用する場合、応答に関係なく後処理を行う必要があり、正しい参照が見つからない場合はアプリがクラッシュし、問題のある場所が絞り込まれます

const results = await Promise.all([
  this.props.client.query({
    query: GET_SPECIAL_DATES,
  }),
  this.props.client.query({
    query: GET_SPECIAL_DATE_TYPES,
  }),
  this.props.client.query({
    query: GET_ORDER_DATES,
  }),
]).catch(e=>console.log(e,"error"));
const specialDates = results[0].data.specialDates;
const specialDateTypes = results[1].data.specialDateTypes;
const orderDates = results[2].data.orders;

-1

それが動作するようPromise.allに設計されている方法です。単一のpromise reject()の場合、メソッド全体がすぐに失敗します。

Promise.allpromiseが失敗することを許可したい場合があります。これを実現するに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);
        });
    });
}

ここでの答えは次のとおりです。stackoverflow.com/questions/31424561/...
Humoyunアフマド

reject()約束で使用しなくても問題ありませんが、別のライブラリの約束を使用する必要がある場合はどうでしょうか。
Dan Dascalescu

-8

私はこの問題をより美しく扱うために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's 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'}

それはどのように機能しますか?関数の実装を示して説明してください。
Bergi、

私はのような新しい並行ロジックを書きましたPromise.all。しかし、それはあらゆる約束のすべてのデータとエラーを収集します。また、オブジェクト入力をサポートしているので、重要ではありません。すべてのデータとエラーを収集した後promise.then、拒否および実行を含む登録済みのコールバックを処理するメソッドをオーバーライドします。詳細については、コード
wenshin

ええと、そのコードは両方呼び出しますonFulfilledonRejectedに渡されるハンドラをthen
ベルギ2016年

はい、場合にのみ、約束の状態が混在fulfilledしてrejected。しかし、実際には、すべてのreturn またはのようにonFulfilled、通常、すべてのpromiseのユースケースと互換性を保つのは難しい問題です。これまでのところ、それを解決する方法はわかりませんが、誰かがより良いアイデアを持っていますか?現時点での最良の答えは問題です。ブラウザ環境でデータとエラーをフィルタリングできない可能性があります。onRejectedPromise.reject()Promise.resolve()
ウェンシン2016年

nipモジュールをpip pythonパッケージマネージャでインストールする必要がありますか?
sevenfourk 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.