Est-ce un anti-pattern d'utiliser async / await dans un nouveau constructeur Promise ()?

93

J'utilise la async.eachLimitfonction pour contrôler le nombre maximum d'opérations à la fois.

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

Comme vous pouvez le voir, je ne peux pas déclarer la myFunctionfonction asynchrone car je n'ai pas accès à la valeur à l'intérieur du deuxième rappel de la eachLimitfonction.

Alexis Tyler
la source
"Comme vous pouvez le voir, je ne peux pas déclarer la myFunction comme asynchrone" --- pouvez-vous en dire plus?
zerkms
1
Oh, ok ... désolé. J'ai besoin du constructeur car j'ai besoin de async.eachLimit pour éviter plus de 500 opérations asynchrones à la fois. Je télécharge et extrait des données de fichiers texte et je veux éviter trop d'opérations asynchrones, après avoir extrait les données, je dois retourner une promesse avec les données, et je ne pourrai pas la renvoyer à partir du rappel de l'async.eachLimit .
1. Pourquoi avez-vous besoin de l'attente? Async est déjà un mécanisme de contrôle de flux. 2. Si vous voulez utiliser async.js avec des promesses dans node.js, jetez un œil à async-q
slebetman
Pour éviter l'enfer des rappels, et si quelque chose se passe, la promesse extérieure attraperait.

Réponses:

81

Vous utilisez effectivement des promesses dans la fonction exécuteur du constructeur de promesse, c'est donc l' anti-modèle du constructeur Promise .

Votre code est un bon exemple du risque principal: ne pas propager toutes les erreurs en toute sécurité. Lisez pourquoi là-bas .

De plus, l'utilisation de async/ awaitpeut rendre les mêmes pièges encore plus surprenants. Comparer:

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

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

avec un asyncéquivalent naïf (faux) :

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

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

Recherchez le dernier dans la console Web de votre navigateur.

Le premier fonctionne car toute exception immédiate dans une fonction exécuteur de constructeur Promise rejette commodément la promesse nouvellement construite (mais à l'intérieur de celle-ci, .thenvous êtes seul).

Le second ne fonctionne pas car toute exception immédiate dans une asyncfonction rejette la promesse implicite renvoyée par la asyncfonction elle-même .

Puisque la valeur de retour d'une fonction exécuteur de constructeur de promesse n'est pas utilisée, c'est une mauvaise nouvelle!

Votre code

Il n'y a aucune raison pour laquelle vous ne pouvez pas définir myFunctioncomme 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.
    });
  });
}

Mais pourquoi utiliser des bibliothèques de contrôle de concurrence obsolètes lorsque vous en avez await?

foc
la source
12
Vous n'avez pas besoin return await: return new Promisec'est suffisant.
lonesomeday
2
J'approuve officiellement cette réponse, j'aurais dit exactement la même chose :-)
Bergi
1
@celoxxx Jetez un œil ici . En effet, vous ne devriez jamais utiliser async.js avec des promesses
Bergi
1
@celoxxx Déposez simplement les types et cela devient js. Vous ne devez pas utiliser async.js car les différentes interfaces - rappels de style nœud vs promesses - provoquent trop de friction et conduisent à un code inutile compliqué et sujet aux erreurs.
Bergi
1
Je suis d'accord avec vous ... Mais ce code est ancien, et je suis en train de refactoriser pour utiliser events + async.js (pour contrôler la limite de l'async, encore. Si vous connaissez une meilleure façon, dites-le).
16

Je suis d'accord avec les réponses données ci-dessus et quand même, il est parfois plus simple d'avoir l'asynchrone dans votre promesse, surtout si vous voulez enchaîner plusieurs opérations en retournant des promesses et éviter l' then().then()enfer. J'envisagerais d'utiliser quelque chose comme ça dans cette situation:

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. La fonction passée à Promise constructeur n'est pas asynchrone, donc les linters ne montrent pas d'erreurs.
  2. Toutes les fonctions asynchrones peuvent être appelées dans un ordre séquentiel en utilisant await .
  3. Des erreurs personnalisées peuvent être ajoutées pour valider les résultats des opérations asynchrones
  4. L'erreur est finalement bien détectée.

Un inconvénient est que vous devez vous rappeler de le mettre try/catchet de le fixer reject.

Vladyslav Zavalykhatko
la source
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);                
        }
    })
}

supprimer wait et async résoudra ce problème. parce que vous avez appliqué l'objet Promise, cela suffit.

Alain
la source
Donc, dans votre exemple, axios.get(url)fonctionnera-t-il comme s'il était appelé await axios.get(url)?
PrestonDocks