webentwicklung-frage-antwort-db.com.de

Eleganter Umgang mit Ablehnung bei Javascript-Versprechen

Ein tolles Muster mit ES2017 async/await ist:

async function () {
  try {
    var result = await some_promised_value()
  } catch (err) {
    console.log(`This block would be processed in
      a reject() callback with promise patterns
      but this is far more intuitive`)
    return false // or something less obtuse
  }
  result = do_something_to_result(result)
  return result;
}

Mit solchen Fehlern umgehen zu können, ist wirklich schön. Angenommen, ich möchte asynchron einen Wert erhalten, den ich vor einer erneuten Zuweisung schützen möchte (z. B. eine Datenbanksitzung), aber ich möchte immer noch das async/await-Muster verwenden (rein intuitiv, da es meiner Meinung nach viel intuitiver ist).

Das Folgende funktioniert nicht, da const Blockbereich hat:

async function () {
  try {
    const result = await get_session()
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
    return false
  }
  // Can't get at result here because it is block scoped.
}

Natürlich können Sie den Rest der Funktion innerhalb des try-Blocks ausführen, aber dann riskieren Sie Fehler, die erwischt werden und an anderer Stelle durchfallen. (Zum Beispiel habe ich mich dieser Herausforderung beim Schreiben von Tests gestellt, bei denen Fehler, wie Fehler beim Test, auf die Testsuite zurückgreifen müssen, aber ich wollte die Treiber-Instantiierung selbst erledigen. Vielleicht gerät ich hier in eine Art Anti-Pattern, indem ich die Fehler nicht zurückfallen lasse zur Testsuite.)

Die einfache Antwort ist offensichtlich, dieses Muster hier nicht zu verwenden. Verwenden Sie stattdessen Versprechen und Rückrufe. Oder verwenden Sie eine var und vermeiden Sie den Blockumfang const und let. Aber ich mag die Vorteile des Umverteilungsschutzes und blockiere den Rahmen für andere Überlegungen.

[question]: Gibt es eine Möglichkeit, einen waitit/async-try/catch-Block für jede Funktion einzugeben? scheint eine mögliche Lösung zu sein, erscheint aber nicht so lesbar wie try/catch. Die Tatsache, dass try/catch den Funktionsumfang nicht unterbricht und ich return innerhalb eines try/catch-Blocks verwenden kann, scheint mir mehr im Einklang mit dem Geist von async/await zu sein, wodurch asynchroner Code eine eher prozedural aussehende Logik erhält.

Idealerweise möchten Sie etwas wie const x = await y() catch (err) oder const x = await y() || fail machen. Ich kann mir aber nichts mit einem ähnlichen Fluss vorstellen, der syntaktisch korrekt ist.

UPDATE: Wie von @ jmar777 vorgeschlagen, ist eine weitere Alternative:

const x = await y().catch(() => {/*handle errors here*/})

Welches ist wahrscheinlich das, was ich den letzten beiden Beispielen in der Praxis am nächsten gefunden habe. In den obigen Beispielen wird jedoch der Bereich return durchbrochen, der die nachgelagerte Ausführung blockiert. Welches ist eine nette synchrone Art, mit Dingen umzugehen.

Jede Lösung hat ihre Kompromisse.

Ich habe die Variablen mit let am oberen Rand des Funktionsbausteins als funktionierende Lösung manuell angehoben (wie in jmar777s Antwort beschrieben), immer noch eine interessante Frage, um die verschiedenen Ansätze zu sehen, also lasse ich die Frage zunächst offen.

13
Tim Hope

Als ich einige Jahre später auf diese Frage zurückkam, ist mir eine viel einfachere Antwort aufgefallen. Ich sehe im Nachhinein nicht, warum ich mich für ein kleines Try/Catch-Paar interessiere und dann etwas mehr Logik außerhalb des Spiels durchführte, wenn ich einfach alles ausführen konnte die Logik und kehren Sie in die try-Anweisung zurück.

Wenn Sie das Beispiel aus der Frage nehmen und diese Struktur anwenden, wäre dies:

 async function () {
  try {
    const result = await get_session()
    // Do what needs to be done and then:
    return result
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
    return false
  }
}

Wenn Sie aus dem try-Block im catch-Block auf etwas zugreifen müssen, führen Sie einen Fehler aus. Es gab möglicherweise einen Grund, warum ich mich aus dem Try-Bereich zurückziehen musste, als ich diese Frage stellte. Ich sehe jedoch kein Szenario, das einen Rückblick erforderlich macht. Diese Lösung würde nicht funktionieren, wenn Sie try/catch/final verwenden und schließlich zurückkehren. Aber ich glaube nicht, dass ich dieses Muster in der Wildnis sehr oft getroffen habe. 

1
Tim Hope

Sie haben möglicherweise ein Missverständnis hinsichtlich der Vorteile von const. Durch die Zuweisung eines Werts mithilfe einer const-Deklaration wird dieser Wert nicht unveränderlich. Dies bedeutet lediglich, dass der Wert dieser Konstante nicht geändert oder erneut deklariert werden kann:

Die const-Deklaration erstellt einen schreibgeschützten Verweis auf einen Wert. Dies bedeutet nicht, dass der Wert, den es enthält, unveränderlich ist, nur dass der Variablenbezeichner nicht erneut zugewiesen werden kann. Wenn der Inhalt beispielsweise ein Objekt ist, bedeutet dies, dass das Objekt selbst noch geändert werden kann. ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const )

In Ihrem Beispiel ist es wahrscheinlich am besten, wenn Sie Ihre result-Bindung manuell außerhalb von try/catch hochziehen:

async function () {
  let result;

  try {
    result = await get_session()
  } catch (err) {
    console.log(`This block should catch any
      instantiation errors.`)
  }

  // do whatever else you need with result here...
}

Eine Alternative wäre, den Code so umzustrukturieren, dass Sie sich überhaupt nicht auf Try/Catch verlassen müssen. Zum Beispiel:

async function () {
  const result = await get_session().catch(someRejectionHandler); 

  // do whatever else you need with result here...
}

Beachten Sie nur, dass Ihr Downstream-Code den Fall, in dem get_session() abgelehnt wurde, und die result aufgrund einer erfolgreichen Antwort nicht initialisiert werden muss, ordnungsgemäß behandeln muss. Dies ist nicht anders als in Ihrem ersten Beispiel, aber beim Scannen des Codes ist dies möglicherweise nicht ganz so offensichtlich.

9
jmar777