Ist es ein Anti-Pattern, async / await in einem neuen Promise () -Konstruktor zu verwenden?

101

Ich verwende die async.eachLimitFunktion, um die maximale Anzahl von Operationen gleichzeitig zu steuern.

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

Wie Sie sehen, kann ich die myFunctionFunktion nicht als asynchron deklarieren, da ich im zweiten Rückruf der eachLimitFunktion keinen Zugriff auf den Wert habe .

Alexis Tyler
quelle
"Wie Sie sehen können, kann ich die myFunction nicht als asynchron deklarieren" --- können Sie mehr erläutern?
Zerkms
1
Oh okay tut mir Leid. Ich brauche den Konstruktor, weil ich das async.eachLimit brauche, um mehr als 500 asynchrone Operationen gleichzeitig zu vermeiden. Ich lade Daten aus Textdateien herunter und extrahiere sie. Ich möchte zu viele asynchrone Vorgänge vermeiden. Nachdem ich die Daten extrahiert habe, muss ich ein Versprechen mit den Daten zurückgeben, und ich kann es nicht aus dem Rückruf von async.eachLimit zurückgeben .
1. Warum brauchst du das Warten? Async ist bereits ein Kontrollflussmechanismus. 2. Wenn Sie async.js mit Versprechungen innerhalb von node.js verwenden möchten, werfen Sie einen Blick auf async-q
slebetman
Um Rückrufhölle zu vermeiden und wenn etwas wirft, würde das äußere Versprechen fangen.

Antworten:

85

Sie verwenden Versprechen effektiv innerhalb der Executor-Konstruktor-Executor-Funktion, also das Anti-Muster des Promise-Konstruktors .

Ihr Code ist ein gutes Beispiel für das Hauptrisiko: Nicht alle Fehler sicher zu verbreiten. Lesen Sie warum dort .

Darüber hinaus kann die Verwendung von async/ awaitdie gleichen Fallen noch überraschender machen. Vergleichen Sie:

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

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

mit einem naiven (falschen) asyncÄquivalent:

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

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

Suchen Sie in der Webkonsole Ihres Browsers nach der letzten.

Die erste funktioniert, weil jede unmittelbare Ausnahme in einer Promise-Konstruktor-Executor-Funktion das neu erstellte Versprechen bequem ablehnt (aber in jeder, die .thenSie alleine sind).

Die zweite Funktion funktioniert nicht, da jede unmittelbare Ausnahme in einer asyncFunktion das implizite Versprechenasync ablehnt, das von der Funktion selbst zurückgegeben wird .

Da der Rückgabewert einer Versprechen-Konstruktor-Executor-Funktion nicht verwendet wird, sind das schlechte Nachrichten!

Dein Code

Es gibt keinen Grund, den Sie nicht definieren können myFunctionals 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.
    });
  });
}

Aber warum sollten Sie veraltete Parallelitätskontrollbibliotheken verwenden, wenn Sie haben await?

Ausleger
quelle
12
Sie brauchen nicht return await: return new Promiseist ausreichend.
einsamer
2
Ich
stimme
1
@celoxxx Schau mal hier . Sie sollten in der Tat niemals async.js mit Versprechungen verwenden
Bergi
1
@celoxxx Lass einfach die Typen fallen und es wird einfach js. Sie sollten async.js nicht verwenden, da die verschiedenen Schnittstellen - Rückrufe im Knotenstil und Versprechen - zu viel Reibung verursachen und zu unnötig kompliziertem und fehleranfälligem Code führen.
Bergi
1
Ich stimme Ihnen zu ... Aber dieser Code ist alt und ich überarbeite die Verwendung von events + async.js (um die Grenze der Asynchronität noch zu kontrollieren. Wenn Sie einen besseren Weg kennen, sagen Sie bitte).
20

Ich stimme den oben gegebenen Antworten zu und trotzdem ist es manchmal besser, Asynchronität in Ihrem Versprechen zu haben, besonders wenn Sie mehrere Operationen verketten möchten, die Versprechen zurückgeben, und die then().then()Hölle vermeiden möchten . Ich würde in Betracht ziehen, in dieser Situation so etwas zu verwenden:

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. Die an den PromiseKonstruktor übergebene Funktion ist nicht asynchron, sodass Linters keine Fehler anzeigen.
  2. Alle asynchronen Funktionen können mit in sequentieller Reihenfolge aufgerufen werden await.
  3. Benutzerdefinierte Fehler können hinzugefügt werden, um die Ergebnisse von asynchronen Vorgängen zu überprüfen
  4. Der Fehler wird irgendwann schön abgefangen.

Ein Nachteil ist jedoch, dass Sie sich daran erinnern müssen, try/catches anzubringen und anzubringen reject.

Vladyslav Zavalykhatko
quelle
4
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);                
        }
    })
}

entfernen warten und asynchron wird dieses Problem lösen. Da Sie das Promise-Objekt angewendet haben, reicht dies aus.

Alain
quelle
1
axios.get(url)Funktioniert in Ihrem Beispiel also so, als ob es so heißt await axios.get(url)?
PrestonDocks