web-dev-qa-db-ja.com

Promise内でawaitを使用する

Promiseのコールバックを非同期として定義しなければならないことに本質的に何か問題があるようです:

return new Promise(async (resolve, reject) => {
  const value = await somethingAsynchronous();
  if (value === something) {
    return resolve('It worked!');
  } else {
    return reject('Nope. Try again.');
  }
});

これは明らかに アンチパターン であり、コーディング それから発生する可能性のある問題 があります。 await/tryブロック内にcatchステートメントを配置した場合でも、ここでエラーを捕捉するのに失敗しやすくなることを理解しています。

私の最初の質問は、別の解決/拒否値でPromiseを転送したい場合、このようなものをコーディングするための最良の方法は何ですか? then/catch?つまり.

return new Promise((resolve, reject) => {
  somethingAsynchronous().then(value => {
    if (value === something) {
      return resolve('It worked!');
    } else {
      return reject('Nope. Try again.');
    }
  }); // errors would now be propagated up
});

または、提案されているように、Promiseコンストラクターから完全に削除しますか here

async function outerFunction() {
  const value = await somethingAsynchronous();
  return new Promise((resolve, reject) => {
    if (value === something) {
      return resolve('It worked!');
    } else {
      return reject('Nope. Try again.');
    }
  });
}

しかし、outerFunction()に複数のawaitステートメントがある場合、つまり、複数の非同期関数を呼び出す線形コードブロックがある場合はどうでしょうか。その後、毎回新しいプロミスを作成して返す必要がありますか?

しかし、このようなコードをどのように説明しますか?

async function outerFunction() {
  if (someSynchronousCheck()) {
    return 'Nope. Try again.' // another reject case
  }

  const value = await somethingAsynchronous();
  // ...
}

これを本来よりも複雑にしているような気がします。コールバックのネスト/ then/catchブロックのネストを避けて、今後問題が発生しないようにしています。

私の最後の質問は、Promiseに渡されるコールバックが本質的にasyncではないのはなぜですか?これはすでにpromiseにラップされており、解決/拒否関数が非同期で呼び出されることを想定しています。

16
Druckles

これをして:

async function outerFunction() {
  const value = await somethingAsynchronous();
  if (value === something) {
    return 'It Worked!';
  }
  throw Error('Nope. Try again.');
}

asyncを使用すると、outerFunctionの結果がPromiseでラップされます。

そのラッピングプロミスが何かに解決することを望む場合は、async関数からそれを返すだけです。ラッピングプロミスを拒否する場合は、async関数内でエラーをスローします。

しかし、このようなコードをどのように説明しますか?

async function outerFunction() {
  if (someSynchronousCheck()) {
    throw Error('Nope. Try again.');
  }

  const value = await somethingAsynchronous();
  // ...
}
25
robertklep

new Promise(async (resolve, reject) => { ... })は比較的新しいアンチパターンです。その結果、1つではなく2つのpromiseオブジェクトが作成され、コンストラクター内で発生するキャッチされないエラーはtry..catchでキャッチできず、処理されない拒否が発生します。

Promise非同期コードはasync..awaitで処理できることを考えると、Promiseコンストラクターの現在の使用例は非promise非同期コードです。

new Promise(resolve => setTimeout(resolve, 1000))

Promiseコンストラクターに同期コードが含まれる場合、または他のpromiseが含まれる場合、promiseはasync関数を使用して作成する必要があります。ドロップイン置換はasync IIFEです。

return (async (resolve, reject) => {
  const value = await somethingAsynchronous();
  if (value === something) {
    return 'It worked!';
  } else {
    throw 'Nope. Try again.';
  }
})();

Promiseと一緒に使用するときにasyncコンストラクターの必要性が依然として存在する場合、Promiseコンストラクターを階層内で下に移動して、async関数をラップしないようにする必要があります。

私の最後の質問は、Promiseに渡されるコールバックが本質的に非同期ではないのはなぜですか?これはすでにpromiseにラップされており、解決/拒否関数が非同期で呼び出されることを想定しています。

async関数は、単に非同期に実行される関数ではなく、利用されることになっている別のpromiseを返します-または少なくともcatchで処理されます。 Promiseは、関数の構築から返されるpromiseを利用するものではありません。

コンストラクターは同じティックで解決でき、必ずしも非同期である必要はありません。

Promise.resolve(1);

と類似しています

Promise(resolve => resolve(1))

そしてしない

Promise(resolve => setTimeout(() => resolve(1)))
6
Estus Flask

これを行うだけで、自分でプロミスを連鎖させることもできます。

return new Promise(async (resolve, reject) => {
  somethingAsynchronous().then((value) => {
      if (value === something) {
        return resolve('It worked!');
      } else {
        return reject('Nope. Try again.');
      }
  }, (error) => { reject(error); });
});

私はしばらくこれを使用してきましたが、それは私にとって完璧に機能します。

0
James Wilkins