Ich weiß, dass dies eine alte Frage ist, aber ich bin gerade über den Thread gestolpert und es scheint hier eine Verschmelzung zwischen Fehlern und Ablehnung zu geben, die (zumindest in vielen Fällen) gegen den oft wiederholten Rat verstößt, keine Ausnahmebehandlung zu verwenden sich mit erwarteten Fällen befassen. Zur Veranschaulichung: Wenn eine asynchrone Methode versucht, einen Benutzer zu authentifizieren, und die Authentifizierung fehlschlägt, ist dies eine Ablehnung (einer von zwei erwarteten Fällen) und kein Fehler (z. B. wenn die Authentifizierungs-API nicht verfügbar war).
Um sicherzustellen, dass ich nicht nur Haare spalte, habe ich mit diesem Code einen Leistungstest mit drei verschiedenen Ansätzen durchgeführt:
const iterations = 100000;
function getSwitch() {
return Math.round(Math.random()) === 1;
}
function doSomething(value) {
return 'something done to ' + value.toString();
}
let processWithThrow = function () {
if (getSwitch()) {
throw new Error('foo');
}
};
let processWithReturn = function () {
if (getSwitch()) {
return new Error('bar');
} else {
return {}
}
};
let processWithCustomObject = function () {
if (getSwitch()) {
return {type: 'rejection', message: 'quux'};
} else {
return {type: 'usable response', value: 'fnord'};
}
};
function testTryCatch(limit) {
for (let i = 0; i < limit; i++) {
try {
processWithThrow();
} catch (e) {
const dummyValue = doSomething(e);
}
}
}
function testReturnError(limit) {
for (let i = 0; i < limit; i++) {
const returnValue = processWithReturn();
if (returnValue instanceof Error) {
const dummyValue = doSomething(returnValue);
}
}
}
function testCustomObject(limit) {
for (let i = 0; i < limit; i++) {
const returnValue = processWithCustomObject();
if (returnValue.type === 'rejection') {
const dummyValue = doSomething(returnValue);
}
}
}
let start, end;
start = new Date();
testTryCatch(iterations);
end = new Date();
const interval_1 = end - start;
start = new Date();
testReturnError(iterations);
end = new Date();
const interval_2 = end - start;
start = new Date();
testCustomObject(iterations);
end = new Date();
const interval_3 = end - start;
console.log(`with try/catch: ${interval_1}ms; with returned Error: ${interval_2}ms; with custom object: ${interval_3}ms`);
Einige der Dinge, die dort drin sind, sind aufgrund meiner Unsicherheit bezüglich des Javascript-Interpreters enthalten (ich gehe immer nur ein Kaninchenloch auf einmal hinunter); Zum Beispiel habe ich die aufgenommendoSomething
Funktion eingeschlossen und ihre Rückgabe zugewiesen, dummyValue
um sicherzustellen, dass die bedingten Blöcke nicht optimiert werden.
Meine Ergebnisse waren:
with try/catch: 507ms; with returned Error: 260ms; with custom object: 5ms
Ich weiß, dass es viele Fälle gibt, in denen es sich nicht lohnt, nach kleinen Optimierungen zu suchen, aber in größeren Systemen können diese Dinge einen großen kumulativen Unterschied bewirken, und das ist ein ziemlich starker Vergleich.
SO… während ich denke, dass der Ansatz der akzeptierten Antwort in Fällen, in denen Sie erwarten, unvorhersehbare Fehler innerhalb einer asynchronen Funktion behandeln zu müssen, vernünftig ist, in Fällen, in denen eine Ablehnung einfach bedeutet, dass Sie sich für Plan B entscheiden müssen (oder C oder D…) "Ich denke, ich würde es vorziehen, die Verwendung eines benutzerdefinierten Antwortobjekts abzulehnen.
Promise
Konstruktor-Antimuster ! Sogar der erste Ausschnitt sollte geschrieben worden seinfoo(id: string): Promise<A> { return someAsyncPromise().then(()=>{ return 200; }, ()=>{ throw 400; }); }