web-dev-qa-db-ja.com

「待機」したJavascriptの約束に対する拒否のエレガントな処理

ES2017 async/awaitの優れたパターンは次のとおりです。

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

そのようなエラーを処理できることは本当に素晴らしいことです。しかし、再割り当てから保護したい値(データベースセッションなど)を非同期で取得したいが、それでも非同期/待機パターンを使用したいとします(これははるかに直感的だと思うので)。

Constはブロックスコープであるため、以下は機能しません。

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.
}

もちろん、tryブロック内で関数の残りの部分を実行することはできますが、エラーが発生して他の場所に移動する危険性があります。 (たとえば、テストの失敗などのエラーがテストスイートにフォールバックする必要があるが、ドライバーのインスタンス化を自分で処理したいというテストを作成するこの課題に挑戦しました。おそらく、これらのエラーをフォールバックさせないことによって、ここで何らかのアンチパターンに陥っています。テストスイートに。)

単純な答えは明らかに、ここではそのパターンを使用しないことです。代わりにPromiseとコールバックを使用してください。または、varを使用して、ブロックスコープのconstおよびletを回避します。しかし、私は他の考慮事項に対して再割り当て保護とブロックスコープの利点を気に入っています。

[質問]: await/async try/catchブロックをすべての関数にラップする方法はありますか? 解決策の1つと思われますが、try/catchほど読みやすいとは言えません。 try/catchは関数のスコープを壊さず、try/catchブロック内からreturnを使用できるという事実は、async/awaitの精神に沿っており、非同期コードに手続き型のロジックを提供しているようです。

理想的には、const x = await y() catch (err)またはconst x = await y() || failのようなものを実行する必要がありますが、構文的に正しい同様のフローを持つものは考えられません。

更新:@ jmar777が提案する別の代替案は次のとおりです。

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

これはおそらく、私が見つけた最後の2つの例に最も近いものです。しかし、上記の例でダウンストリームの実行をブロックするreturnスコープを壊します。これは、同期に似た方法で処理する方法です。

各ソリューションにはトレードオフがあります。

関数ブロックの先頭にあるletを使用して手動で変数を引き上げましたが(jmar777の回答で説明されているように)、さまざまなアプローチを見るのに興味深い質問なので、今は質問を開いたままにしておきます。

18
Tim Hope

数年後にこの質問に戻ると、はるかに単純な答えが私に起こりました、なぜ私が小さなtry/catchペアに興味があり、単純にすべてを実行できるのに、その外側でより多くのロジックを実行したのか振り返ってみませんロジックとtryステートメント内に戻ります。

質問から例を取り、その構造を適用すると、次のようになります。

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

Catchブロックのtryブロックから何かにアクセスする必要がある場合は、エラーで発生させます。この質問をしたときにtryスコープの外に移動する必要があった理由はあるかもしれませんが、振り返ってみる必要があるシナリオがわかりません。このソリューションは、try/catch/finallyを使用して、最終的に戻る場合には機能しません。しかし、実際にそのパターンに遭遇したことはほとんどないと思います。

1
Tim Hope

constを使用する利点について誤解しているかもしれません。 const宣言を使用して値を割り当てても、その値は不変になりません。単に、その定数の値を変更または再宣言できないことを意味します。

Const宣言は、値への読み取り専用の参照を作成します。それが保持する値が不変であることを意味するのではなく、変数識別子を再割り当てできないということだけです。たとえば、コンテンツがオブジェクトの場合、これはオブジェクト自体を変更できることを意味します。 ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/const

あなたの例では、おそらく、try/catchの外でresultバインディングを手動で巻き上げるのが最善でしょう。

_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...
}
_

別の方法は、try/catchにまったく依存する必要がないようにコードを再構成することです。例えば:

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

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

ダウンストリームコードは、get_session()が拒否され、正常な応答に基づいてresultが初期化されない場合を適切に処理する必要があることに注意してください。これは最初の例と同じですが、コードをスキャンするときにそれほど明白ではないかもしれません。

10
jmar777