新しいPromise()コンストラクター内でasync / awaitを使用するのはアンチパターンですか?


102

このasync.eachLimit関数を使用して、一度に最大操作数を制御しています。

const { eachLimit } = require("async");

function myFunction() {
 return new Promise(async (resolve, reject) => {
   eachLimit((await getAsyncArray), 500, (item, callback) => {
     // do other things that use native promises.
   }, (error) => {
     if (error) return reject(error);
     // resolve here passing the next value.
   });
 });
}

ご覧のとおり、myFunction関数の2番目のコールバック内の値にアクセスできないため、関数を非同期として宣言できませんeachLimit


「ご覧のとおり、myFunctionを非同期として宣言することはできません」---さらに詳しく説明していただけますか?
zerkms 2017年

1
ああ、わかりました...ごめんなさい。一度に500を超える非同期操作を回避するには、async.eachLimitが必要なので、コンストラクターが必要です。テキストファイルからデータをダウンロードして抽出していますが、非同期操作をあまり避けたいのですが、データを抽出した後、データを含むPromiseを返す必要があり、async.eachLimitのコールバックから返すことができません。 。

1.なぜあなたは待つ必要があるのですか?非同期はすでに制御フローメカニズムです。2. node.js内でpromiseを使用してasync.jsを使用する場合は、async-qを参照してください
slebetman 2017年

コールバック地獄を回避するために、そして何かがスローされた場合、外側の約束がキャッチされます。

回答:


87

promiseコンストラクターエグゼキューター関数内でpromiseを効果的に使用しているので、これはPromiseコンストラクターのアンチパターンです。

あなたのコードは主なリスクの良い例です:すべてのエラーを安全に伝播しないこと。なぜそこにあるのかを読んでください。

さらに、async/awaitを使用すると、同じトラップがさらに驚くべきものになる可能性があります。比較:

let p = new Promise(resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Catches it.

ナイーブな(間違った)async同等のもの:

let p = new Promise(async resolve => {
  ""(); // TypeError
  resolve();
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e)); // Doesn't catch it!

最後のものについては、ブラウザのWebコンソールを調べてください。

最初のものは、Promiseコンストラクターエグゼキューター関数の即時例外が新しく構築されたpromiseを都合よく拒否するために機能.thenします(ただし、自分自身の内部では)。

async関数内の即時例外はasync関数自体によって返される暗黙のpromiseを拒否するため、2番目のものは機能しません。

promiseコンストラクターエグゼキューター関数の戻り値は使用されていないので、それは悪いニュースです!

あなたのコード

あなたが定義することができない理由はありませんmyFunctionようにasync

async function myFunction() {
  let array = await getAsyncArray();
  return new Promise((resolve, reject) => {
    eachLimit(array, 500, (item, callback) => {
      // do other things that use native promises.
    }, error => {
      if (error) return reject(error);
      // resolve here passing the next value.
    });
  });
}

持っているのになぜ古い同時実行制御ライブラリを使用するのawaitですか?


12
あなたは必要ありませんreturn awaitreturn new Promiseで十分です。
lonesomeday 2017年

2
私はこの答えを公式に承認します、私はまったく同じことを言ったでしょう:-)
ベルギ2017年

1
@celoxxxこちらをご覧ください。あなたは確かに約束してasync.jsを使用しないでください
Bergi

1
@celoxxxタイプを削除するだけで、プレーンなjsになります。異なるインターフェース(ノードスタイルのコールバックとpromise)は摩擦が大きすぎて、不必要に複雑でエラーが発生しやすいコードにつながるため、async.jsを使用しないでください。
ベルギ2017年

1
私はあなたに同意します...しかし、このコードは古いので、events + async.jsを使用するようにリファクタリングしています(非同期の制限を制御するためです。もっと良い方法を知っている場合は、言ってください)。

20

私は上記の答えに同意しますが、それでも、特に約束を返す複数の操作を連鎖させてthen().then()地獄を避けたい場合は、約束の中に非同期を含める方が良い場合があります。そのような状況では、次のようなものを使用することを検討します。

const operation1 = Promise.resolve(5)
const operation2 = Promise.resolve(15)
const publishResult = () => Promise.reject(`Can't publish`)

let p = new Promise((resolve, reject) => {
  (async () => {
    try {
      const op1 = await operation1;
      const op2 = await operation2;

      if (op2 == null) {
         throw new Error('Validation error');
      }

      const res = op1 + op2;
      const result = await publishResult(res);
      resolve(result)
    } catch (err) {
      reject(err)
    }
  })()
});

(async () => {
  await p;
})().catch(e => console.log("Caught: " + e));
  1. Promiseコンストラクターに渡される関数は非同期ではないため、リンターはエラーを表示しません。
  2. すべての非同期関数は、を使用して順番に呼び出すことができますawait
  3. カスタムエラーを追加して、非同期操作の結果を検証できます
  4. エラーは最終的にうまくキャッチされます。

ただし、欠点はtry/catch、に取り付けて取り付けることを覚えておく必要があることですreject


3
static getPosts(){
    return new Promise( (resolve, reject) =>{
        try {
            const res =  axios.get(url);
            const data = res.data;
            resolve(
                data.map(post => ({
                    ...post,
                    createdAt: new Date(post.createdAt)
                }))
            )
        } catch (err) {
            reject(err);                
        }
    })
}

awaitを削除すると、asyncがこの問題を解決します。Promiseオブジェクトを適用したので、それで十分です。


1
したがって、あなたの例では、axios.get(url)として呼び出されたawait axios.get(url)かのように機能しますか?
PrestonDocks
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.