wake-up-neo.com

Wie lehne ich die async/await-Syntax ab?

Wie kann ich ein Versprechen ablehnen, das von einer asynchronen/wartenden Funktion zurückgegeben wird?

z.B. Ursprünglich

foo(id: string): Promise<A> {
  return new Promise((resolve, reject) => {
    someAsyncPromise().then((value)=>resolve(200)).catch((err)=>reject(400))
  });
}

Übersetzen Sie in async/await

async foo(id: string): Promise<A> {
  try{
    await someAsyncPromise();
    return 200;
  } catch(error) {//here goes if someAsyncPromise() rejected}
    return 400; //this will result in a resolved promise.
  });
}

Wie konnte ich dieses Versprechen in diesem Fall also richtig ablehnen?

148
Phoenix

Am besten verwenden Sie throw einen Error, der den Wert umgibt, was zu einem abgelehnten Versprechen mit einem Error führt, der den Wert umgibt:

} catch (error) {
    throw new Error(400);
}

Sie können auch nur den Wert throw eingeben, aber es gibt keine Stack-Trace-Informationen:

} catch (error) {
    throw 400;
}

Alternativ können Sie eine abgelehnte Zusage mit einer Error zurückgeben, die den Wert umgibt:

} catch (error) {
    return Promise.reject(new Error(400));
}

(Oder einfach nur return Promise.reject(400);, aber dann gibt es keine Kontextinformationen.)

(In Ihrem Fall, da Sie TypeScript und foos retrn-Wert Promise<A> verwenden, würden Sie return Promise.reject<A>(400 /*or error*/); verwenden.)

In einer async/await-Situation ist diese letzte Situation wahrscheinlich ein bisschen eine semantische Fehlanpassung, aber sie funktioniert.

Wenn Sie eine Error werfen, spielt das gut mit allem, was Ihr foo-Ergebnis mit der await-Syntax verbraucht:

try {
    await foo();
} catch (error) {
    // Here, `error` would be an `Error` (with stack trace, etc.).
    // Whereas if you used `throw 400`, it would just be `400`.
}
184
T.J. Crowder

Wahrscheinlich sollte auch erwähnt werden, dass Sie eine catch()-Funktion nach dem Aufruf Ihrer asynchronen Operation einfach verketten können, da unter der Haube noch ein Versprechen zurückgegeben wird.

await foo().catch(error => console.log(error));

Auf diese Weise können Sie die try/catch-Syntax vermeiden, wenn Sie sie nicht mögen.

96
David

Sie können eine wrapper-Funktion erstellen, die ein promise enthält und ein Array mit Daten zurückgibt, wenn kein Fehler vorliegt, und den Fehler, wenn ein Fehler aufgetreten ist.

function safePromise(promise) {
  return promise.then(data => [ data ]).catch(error => [ null, error ]);
}

Verwenden Sie es wie folgt in ES7 und in einer async - Funktion:

async function checkItem() {
  const [ item, error ] = await safePromise(getItem(id));
  if (error) { return null; } // handle error and return
  return item; // no error so safe to use item
}
8
Andy

Eine bessere Möglichkeit, die async-Funktion zu schreiben, besteht darin, ein ausstehendes Versprechen von Anfang an zurückzugeben und dann sowohl Zurückweisungen als auch Auflösungen innerhalb des Rückrufs des Versprechens zu behandeln, anstatt nur ein abgelehntes Versprechen auf Fehler auszusprechen. Beispiel:

async foo(id: string): Promise<A> {
    return new Promise(function(resolve, reject) {
        // execute some code here
        if (success) { // let's say this is a boolean value from line above
            return resolve(success);
        } else {
            return reject(error); // this can be anything, preferably an Error object to catch the stacktrace from this function
        }
    });
}

Dann ketten Sie einfach Methoden auf das zurückgegebene Versprechen:

async function bar () {
    try {
        var result = await foo("someID")
        // use the result here
    } catch (error) {
        // handle error here
    }
}

bar()

Quelle - dieses Tutorial: 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

4
OzzyTheGiant

Dies ist keine Antwort über @ T.J. Crowder ist einer. Nur ein Kommentar zu dem Kommentar: "Wenn die Ausnahme tatsächlich in eine Ablehnung umgewandelt wird, bin ich mir nicht sicher, ob es mich wirklich stört, ob es sich um einen Fehler handelt. Meine Gründe, nur Fehler zu werfen, treffen wahrscheinlich nicht zu. "

wenn Ihr Code async/await verwendet, ist es dennoch empfehlenswert, eine Error anstelle von 400 abzulehnen:

try {
  await foo('a');
}
catch (e) {
  // you would still want `e` to be an `Error` instead of `400`
}
2
unional

Ich weiß, dass dies eine alte Frage ist, aber ich bin gerade über den Faden gestolpert und es scheint hier ein Konflikt zwischen Fehlern und Ablehnung zu geben, der (in vielen Fällen zumindest) des oft wiederholten Rats, keine Ausnahmebehandlung zu verwenden, läuft mit erwarteten Fällen umgehen. 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 sicherzugehen, dass ich nicht nur Haare spalte, habe ich einen Performance-Test mit drei verschiedenen Ansätzen durchgeführt, wobei ich diesen Code verwendet habe:

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 Dinge, die dort enthalten sind, sind enthalten, weil ich unsicher bin, was den Javascript-Interpreter angeht (ich möchte nur immer nur ein Kaninchenloch hinuntergehen). Ich habe beispielsweise die Funktion doSomething hinzugefügt und deren Rückkehr dummyValue zugewiesen, 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 nicht die Mühe wert ist, nach kleinen Optimierungen zu suchen, aber in größeren Systemen können diese Dinge einen großen kumulativen Unterschied ausmachen, und das ist ein ziemlich krasser Vergleich.

SO ... während ich denke, der Ansatz der akzeptierten Antwort ist solide in Fällen, in denen Sie erwarten, mit unvorhersehbaren Fehlern innerhalb einer asynchronen Funktion umzugehen, und in Fällen, in denen eine Ablehnung einfach bedeutet: "Sie müssen mit Plan B (oder C oder D…) "Ich denke, ich würde es vorziehen, ein benutzerdefiniertes Antwortobjekt abzulehnen.

1
RiqueW

Ich habe einen Vorschlag, Ablehnungen in einem neuartigen Ansatz richtig zu behandeln, ohne mehrere Try-Catch-Blöcke zu haben.

import to from './to';

async foo(id: string): Promise<A> {
    let err, result;
    [err, result] = await to(someAsyncPromise()); // notice the to() here
    if (err) {
        return 400;
    }
    return 200;
}

Wo soll die Funktion to.ts importiert werden aus:

export default function to(promise: Promise<any>): Promise<any> {
    return promise.then(data => {
        return [null, data];
    }).catch(err => [err]);
}

Credits gehen an Dima Grossman im folgenden link .

0
Pedro Lourenço