web-dev-qa-db-ja.com

Promise.allでのエラー処理

Promise.all(arrayOfPromises)で解決しているPromiseの配列があります。

私は約束の連鎖を続けます。こんな感じ

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

エラーが発生した場合に個々のpromiseを処理するための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;
    });

.thenの前にPromise.allの.catchを配置すると、元のpromiseからのエラーをすべて検出した後、配列全体を次の.thenに戻すという目的にかなっているようです。

ありがとうございます。

174
Jon

Promise.allは全部か無かです。配列内のすべての約束が解決されるか、またはoneが拒否されるとすぐに拒否されます。つまり、解決されたすべての値の配列で解決するか、または単一のエラーで拒否します。

いくつかのライブラリはPromise.whenと呼ばれるものを持っています、私は代わりに解決するか拒絶するかの代わりにallの約束を待つでしょう、しかし私はそれに精通していませんES6にはありません。

あなたのコード

私はあなたの修正がうまくいくはずであることにここで他の人と同意します。成功した値とエラーのオブジェクトが混在している可能性がある配列で解決する必要があります。エラーオブジェクトをsuccess-pathで渡すことは珍しいことですが、あなたのコードがそれらを期待していると仮定すれば、私はそれについて問題ないと思います。

それが「解決しない」理由を私が考えることができる唯一の理由は、あなたが私たちに見せていないコードで失敗しているということと、これに関するエラーメッセージを見ていない理由です。捕まえる(とにかくあなたが私たちに見せているものまで)。

私はあなたの例から「既存のチェーン」を取り除き、チェーンをキャッチで終了させる自由を取った。これはあなたにとって正しいことではないかもしれませんが、これを読んでいる人にとっては、常にチェーンを返すか終了させること、あるいはコーディングエラーでさえ潜在的なエラーが隠されることが重要です。

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

新しい答え

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));
59
Solominh

Promise.allループを続けるために(Promiseが拒否した場合でも)、私はexecuteAllPromisesと呼ばれるユーティリティ関数を書きました。このユーティリティ関数は、resultserrorsを持つオブジェクトを返します。

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

最後のステップとして、executeAllPromisesはラップされたpromiseのすべての値を累積し、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}`);
});
16

非同期を使用する -

ここでは、非同期関数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]です。

6
Nayan Patel

あなたが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();
});
6
Mohamed Mahmoud

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

@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
  })
3
Herman

個々の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) );
}
3
Nayan Patel

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

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

MDNのドキュメントから

finally()メソッドは、結果がどうであれ、約束が決まったら何らかの処理やクリーンアップをしたい場合に便利です。

finally()メソッドは.then(onFinally, onFinally)の呼び出しと非常によく似ていますが、いくつかの違いがあります。

関数をインラインで作成するときは、2回宣言するか、変数を作成することを強制されるのではなく、1回渡すことができます。

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

Promise.resolve(2).then(() => {}, () => {})(undefinedで解決される)とは異なり、Promise.resolve(2).finally(() => {})は2で解決されます。同様に、Promise.reject(3).then(() => {}, () => {})(undefinedで満たされる)とは異なり、Promise.reject(3).finally(() => {})は3で拒否されます。

==フォールバック==

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

1
Tom Auger

失敗をキャッチし、代わりに合意された値(例: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'};
    }

}
0
Tamir Nakar

あるいは、1つの失敗があっても解決済みのpromiseの値を特に気にしなくても実行したい場合は、次のようにしてpromiseを解決します。それらはすべて成功し、いずれかが失敗すると失敗した約束を拒否します。

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
Eric