Async: Traiter les fonctions sans rappel comme des promesses

Créé le 9 juil. 2019  ·  12Commentaires  ·  Source: caolan/async

Le comportement actuel est très surprenant :

await async.parallel({foo: async()=>Promise.all([1,2])})
// { foo: [ 1, 2 ] }
await async.parallel({foo: ()=>Promise.all([1,2])})
// waits forever because non-async function expects a ballback

Parce que pour les fonctions courtes, il est tentant d'écrire simplement foo: () => doSomething() . Il est très facile d'y oublier le mot-clé async

Mais actuellement, cela ne fonctionnera pas, il doit être écrit sous la forme foo: async () => doSomething()

Pourrions-nous plutôt vérifier si un rappel est passé ou non, au lieu de vérifier si la fonction est une AsyncFunction ?

wont fix

Commentaire le plus utile

Même avec ce problème fermé, à partir du problème lié ci-dessus, j'aimerais insister à nouveau sur les déclarations
Le comportement actuel m'a beaucoup dérouté, et bien que je comprenne (en quelque sorte) les limitations techniques discutées ici, je pense que la documentation n'est tout simplement pas conforme au comportement actuel.
Pour, par exemple, mapLimit, il est dit "Retours : une promesse, si aucun rappel n'est passé" - j'ai commencé avec la version de rappel, puis j'ai fondamentalement omis le rappel et j'ai été surpris de ne pas recevoir la promesse (parce que je n'avais pas le mot-clé 'async').
de plus, l'instruction 'callback' dans la fonction iteratee n'est pas nécessaire alors, à la place vous devez 'retourner' une valeur.
Ainsi, soit le comportement doit être modifié (préféré), soit au moins la documentation et les exemples doivent être alignés à mon point de vue pour éviter toute confusion.

Tous les 12 commentaires

Nous n'avons aucun moyen de détecter de manière fiable si une fonction renvoie une promesse ou non, et si nous devons lui passer un rappel ou non au préalable. ( Function.length ou l'analyse de Function.toString() n'est pas assez bonne) C'est ce qu'est asyncify -- convertir les fonctions de retour de promesse en fonctions qui prennent un rappel. Puisque nous pouvons détecter les fonctions async puisqu'elles ont un type différent, nous pouvons automatiquement les asyncify (c'est ainsi que cela fonctionne en interne). Mais pour les fonctions ordinaires qui renvoient une promesse, nous n'avons aucun moyen de savoir avant qu'Async ne les appelle.

@aearly hmm right, alors peut-être quelque chose de plus simple comme détecter le rappel au niveau supérieur ?

async function parallel(o, cb) {
  if (!cb) { // promise behavior
    return Object.fromEntries(
      await Promise.all(Object.entries(o).map(async ([k, v]) => [k, await v()]))
    )
  }
  // else do the callback behavior
}

Nous avons déjà fait quelque chose comme ça -- retourner une promesse si vous omettez le rappel à async.parallel . Nous ne supposons pas non plus que les fonctions que vous lui transmettez renvoient des promesses si vous omettez le rappel, car nous n'avons aucun moyen de supposer l'intention de l'utilisateur. Peut-être que je comprends mal ce que tu veux dire ?

Ok, soyons concrets : https://repl.it/@caub/async -async

// npm.im/async expects all inner functions to be async,
// even when using no outer callback (as 2nd arg of async.parallel)
console.log(
  await async.parallel({
    foo: async () => delay(1000).then(() => 1),
    bar: async (rs) => {
      return 2
    },
  })
);

// Doesn't work without those async,
// but I'd like this to resolve just like above instead of freezing
console.log(
  await async.parallel({
    foo: () => delay(1000).then(() => 1),
    bar: () => {
      return 2
    },
  })
);

Je pense que, dans une future prochaine version majeure, il pourrait être intéressant de ne prendre en charge que le rappel d'une manière similaire à celle-ci :

await Promise.all([
  { then(resolve){ setTimeout(resolve, 50, 1) } }, // callback way
  2,
  delay(45).then(())=>3)
])
// [1, 2, 3]

suppression du besoin async mots-clés

L'avantage est plus de clarté. Je comprends votre point de ne pas supposer l'intention de l'utilisateur, mais vous supposez toujours que les gens n'oublieront pas le mot-clé async lorsqu'ils souhaitent utiliser la résolution de promesse

Cela briserait la compatibilité descendante d'une manière assez énorme -- par exemple, vous ne pourriez plus faire des choses comme Async.map(fileNames, fs.readFile) .

Pourriez-vous expliquer où il se briserait?

Ce qui casse actuellement est :

await async.map(['server.js', 'package.json'], fs.readFile) // works
await async.map(['server.js', 'package.json'], fs.promises.readFile) // works
await async.map(['server.js', 'package.json'], s => fs.promises.readFile(s)) // doesn't work
await async.map(['server.js', 'package.json'], async s => fs.promises.readFile(s)) // works

L'idée est de réparer le 3ème

J'ai constamment ces problèmes, par exemple j'ai écrit :

  // ...
  doc = await async.map(items, item => item.toDoc(currentUser, options));

quelque part, et encore, timeout parce qu'il me manquait un async item => item.toDoc..

Si je suis seul dans ce cas, ça ne vaut pas la peine, mais si plus de gens sont dans cette situation, je pense que ça vaut la peine de considérer le changement proposé

Cet exemple avec async.map est également très représentatif, puisque vous pouvez voir mon point avec comment

  doc = await Promise.all(items.map(item => item.toDoc(currentUser, options)));

fonctionne sans avoir besoin d'un async item => item.toDoc..

Il n'y a aucun moyen de réparer de manière fiable le 3ème, cela a été beaucoup discuté dans le passé. Nous ne pouvons pas vérifier la valeur de retour d'une fonction, puis rétroactivement ne pas passer de rappel à la fonction. Le mot-clé async est aussi bon que possible.

@caub Shameless branche ici, mais je pense que asyncp a la capacité de gérer ce que vous recherchez ici.

Même avec ce problème fermé, à partir du problème lié ci-dessus, j'aimerais insister à nouveau sur les déclarations
Le comportement actuel m'a beaucoup dérouté, et bien que je comprenne (en quelque sorte) les limitations techniques discutées ici, je pense que la documentation n'est tout simplement pas conforme au comportement actuel.
Pour, par exemple, mapLimit, il est dit "Retours : une promesse, si aucun rappel n'est passé" - j'ai commencé avec la version de rappel, puis j'ai fondamentalement omis le rappel et j'ai été surpris de ne pas recevoir la promesse (parce que je n'avais pas le mot-clé 'async').
de plus, l'instruction 'callback' dans la fonction iteratee n'est pas nécessaire alors, à la place vous devez 'retourner' une valeur.
Ainsi, soit le comportement doit être modifié (préféré), soit au moins la documentation et les exemples doivent être alignés à mon point de vue pour éviter toute confusion.

@caub C'est la raison
https://github.com/caolan/async/blob/8aecf108b3922bc5211036706a0f6f75e02bd42b/lib/internal/wrapAsync.js#L3 :L5

function isAsync(fn) {
    return fn[Symbol.toStringTag] === 'AsyncFunction';
}

De cette façon, il est plus facile de vérifier si la fonction renvoie une promesse sans l'appeler réellement. Mais restez d'accord avec vous pour dire que cela va à l'encontre des conventions async/wait. Comme ajouter un mot-clé async sans await interne est considéré comme une mauvaise pratique dans la plupart des cas en fait.

de https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function

Le corps d'une fonction async peut être considéré comme étant divisé par zéro ou plusieurs expressions d'attente. Le code de niveau supérieur, jusqu'à et y compris la première expression d'attente (le cas échéant), est exécuté de manière synchrone.

... et donc ce sera inutile et pire encore, cela ajoutera des frais généraux de performances si le code est transpilé par babel par exemple, car cela ajoutera plus de code et d'étapes simplement à cause de l'existence du mot-clé async

Par exemple, les éléments suivants :

async function foo() {
   return 1
}

...est équivalent à:

function foo() {
   return Promise.resolve(1)
}

mais avec cette bibliothèque,

async.mapLimit([1], 1, async v => v)

et

async.mapLimit([1], 1, v => Promise.resolve(v)))

ne sont pas équivalents !

Cette page vous a été utile?
0 / 5 - 0 notes