wake-up-neo.com

Fehler in Promise.all behandeln

Ich habe eine Reihe von Versprechungen, die ich mit Promise.all (arrayOfPromises) auflöse.

Ich mache weiter, um die Versprechenkette fortzusetzen. Sieht so aus

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

Ich möchte eine catch-Anweisung hinzufügen, um ein einzelnes Versprechen für den Fall zu behandeln, dass Fehler auftreten. Wenn ich es jedoch versuche, gibt Promise.all den ersten gefundenen Fehler zurück (ignoriert den Rest), und dann kann ich die Daten nicht vom Rest erhalten die Versprechen im Array (das nicht fehlerhaft ist).

Ich habe versucht, etwas zu tun wie ..

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

Aber das löst sich nicht auf.

Vielen Dank!

-

Bearbeiten:

Was die Antworten unten sagten, war absolut richtig, der Code brach aus anderen Gründen. Wenn jemand interessiert ist, ist dies die Lösung, mit der ich am Ende ...

Knoten Express Server-Kette

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-Aufruf (route.async-Aufruf)

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

Wenn Sie .catch für Promise.all vor das. Setzen, scheint es der Zweck zu sein, Fehler von den ursprünglichen Versprechungen abzufangen, aber dann das gesamte Array an das nächste zurückzugeben

Vielen Dank! 

144
Jon

Promise.all ist alles oder nichts. Es löst sich auf, sobald alle Versprechen im Array aufgelöst sind oder abgelehnt werden, sobald Eins von ihnen abgelehnt wird. Mit anderen Worten, es wird entweder mit einem Array aller aufgelösten Werte aufgelöst oder mit einem einzelnen Fehler zurückgewiesen.

Einige Bibliotheken haben etwas namens Promise.when, von dem ich verstehe, dass es stattdessen warten würde, dass all verspricht, das Array entweder aufzulösen oder abzulehnen, aber ich bin nicht damit vertraut, und es ist nicht in ES6.

Dein Code

Ich stimme mit anderen hier überein, dass Ihr Fix funktionieren sollte. Es sollte mit einem Array aufgelöst werden, das eine Mischung erfolgreicher Werte und Fehlerobjekte enthalten kann. Es ist ungewöhnlich, Fehlerobjekte im Erfolgspfad zu übergeben. Wenn Sie jedoch davon ausgehen, dass Ihr Code sie erwartet, sehe ich kein Problem.

Der einzige Grund, aus dem ich mir vorstellen kann, warum es "nicht aufgelöst" werden könnte, ist der, dass es im Code fehlschlägt, den Sie uns nicht anzeigen. Der Grund, warum Sie keine Fehlermeldung darüber erhalten, ist, dass diese Versprechenkette nicht mit einem finalen Ergebnis beendet wird fangen (soweit, was Sie uns sowieso zeigen).

Ich habe mir die Freiheit genommen, die "bestehende Kette" aus Ihrem Beispiel herauszulösen und die Kette mit einem Haken zu beenden. Dies ist möglicherweise nicht das Richtige für Sie, aber für diejenigen, die dies lesen, ist es wichtig, Ketten immer zurückzugeben oder zu beenden. Andernfalls werden potenzielle Fehler oder sogar Codierungsfehler ausgeblendet (was ich vermute, dass hier etwas passiert ist):

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

NEUE ANTWORT

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

ALTE ANTWORT

Wir müssen benutzerdefinierte Promise.all () ..__ schreiben. Hier die Lösung, die ich in meinem Projekt verwende. Der Fehler wird als normales Ergebnis zurückgegeben. Nach Beendigung aller Versprechen können wir den Fehler herausfiltern.

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

Um die Promise.all-Schleife fortzusetzen (auch wenn ein Promise ablehnt), habe ich eine Utility-Funktion geschrieben, die executeAllPromises heißt. Diese Utility-Funktion gibt ein Objekt mit results und errors zurück.

Die Idee ist, dass alle Versprechen, die Sie an executeAllPromises übergeben, in ein neues Versprechen verpackt werden, das immer aufgelöst wird. Das neue Versprechen wird mit einem Array aufgelöst, das 2 Punkte hat. Der erste Punkt enthält den Auflösungswert (falls vorhanden) und der zweite Punkt den Fehler (wenn das umwickelte Versprechen ablehnt). 

Als letzten Schritt sammelt executeAllPromises alle Werte der umschlossenen Versprechen und gibt das letzte Objekt mit einem Array für results und einem Array für errors zurück.

Hier ist der Code:

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

15

Verwenden von Async wait -

hier gibt eine asynchrone Funktion func1 einen aufgelösten Wert zurück, und func2 löst einen Fehler aus und gibt in dieser Situation eine Null zurück. Wir können damit umgehen, wie wir wollen, und entsprechend zurückkehren.

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

Ausgabe ist - ['func1', null]

6
Nayan Patel

wenn Sie die q library https://github.com/kriskowal/q verwenden, verfügt sie über die Methode q.allSettled (), die dieses Problem lösen kann Geben Sie entweder "fullfiled" oder "abgelehnt" an 

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

Für diejenigen, die ES8 verwenden, die hier stolpern, können Sie Folgendes tun, indem Sie async-Funktionen verwenden:

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);
2
Tyler Yasaka

Wir können die Ablehnung auf der Ebene der einzelnen Versprechen behandeln. Wenn wir also die Ergebnisse in unserem Ergebnis-Array erhalten, wird der abgelehnte Array-Index undefined sein. Wir können mit dieser Situation nach Bedarf umgehen und die verbleibenden Ergebnisse verwenden.

Hier habe ich das erste Versprechen abgelehnt, es kommt also undefiniert, aber wir können das Ergebnis des zweiten Versprechens verwenden, das sich auf Index 1 befindet.

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

2
Nayan Patel

Haben Sie Promise.prototype.finally() in Betracht gezogen?

Es scheint genau das zu sein, was Sie wollen: Führen Sie eine Funktion aus, sobald alle Versprechen erfüllt sind (gelöst/abgelehnt), unabhängig davon, ob einige der Versprechen abgelehnt werden.

Aus der MDN-Dokumentation :

Die finally()-Methode kann nützlich sein, wenn Sie die Verarbeitung oder Bereinigung durchführen möchten, sobald das Versprechen erfüllt ist, unabhängig von dessen Ergebnis.

Die finally()-Methode ist dem Aufruf von .then(onFinally, onFinally) sehr ähnlich, es gibt jedoch einige Unterschiede:

Wenn Sie eine Funktion inline erstellen, können Sie sie einmal übergeben, anstatt sie zweimal zu deklarieren oder eine Variable dafür zu erstellen.

Ein Callback am Schluss wird keine Argumente erhalten, da es keine zuverlässige Methode gibt, um festzustellen, ob die Zusage erfüllt oder abgelehnt wurde. Dieser Anwendungsfall ist genau dann der Fall, wenn Sie sich nicht für den Ablehnungsgrund oder den Erfüllungswert interessieren und es daher nicht erforderlich ist, ihn anzugeben.

Anders als Promise.resolve(2).then(() => {}, () => {}) (das mit undefined aufgelöst wird) wird Promise.resolve(2).finally(() => {}) mit 2 ..__ aufgelöst. Ähnlich wie Promise.reject(3).then(() => {}, () => {}) (das mit undefined erfüllt wird) wird Promise.reject(3).finally(() => {}) mit 3 abgelehnt.

== Rückfall ==

Wenn Ihre JavaScript-Version Promise.prototype.finally() nicht unterstützt, können Sie diese Problemumgehung von Jake Archibald : Promise.all(promises.map(p => p.catch(() => undefined))); verwenden.

1
Tom Auger

Wie @jib sagte:

Promise.all ist alles oder nichts.

Sie können jedoch bestimmte Versprechungen kontrollieren, deren Versagen "erlaubt" ist, und wir möchten mit .then fortfahren.

Zum Beispiel.

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

Sie können Ihre Funktionen zur Rückgabe von Versprechungen immer so verpacken, dass sie einen Fehler abfangen und stattdessen einen vereinbarten Wert (z. B. error.message) zurückgeben, sodass die Ausnahme nicht bis zur Promise.all-Funktion reicht und diese deaktiviert.

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

Wenn Sie einen Fall haben, in dem Ihnen die Werte der aufgelösten Versprechen nicht besonders wichtig sind, wenn es einen Fehler gibt, Sie aber dennoch möchten, dass sie ausgeführt werden, können Sie so etwas tun, das sich mit den Versprechen wie gewohnt auflöst Sie alle haben Erfolg und lehnen die fehlgeschlagenen Versprechen ab, wenn eines von ihnen versagt:

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