web-dev-qa-db-fra.com

Comment utiliser Array.prototype.filter avec async?

Contexte

J'essaie de filtrer un tableau d'objets. Avant de filtrer, je dois les convertir au format souhaité et cette opération est asynchrone. 

 const convert = () => new Promise( resolve => {
     setTimeout( resolve, 1000 );
 });

Donc, mon premier essai a été de faire quelque chose comme ce qui suit en utilisant async/wait:

const objs = [ { id: 1, data: "hello" }, { id: 2, data: "world"} ];

objs.filter( async ( obj ) => {
    await convert();
    return obj.data === "hello";
});

Comme certains d'entre vous le savent peut-être, Array.protoype.filter est une fonction pour laquelle callback doit renvoyer true ou false. filter est synchrone. Dans l'exemple précédent, je n'en renvoie aucun, je retourne une promesse (toutes les fonctions asynchrones sont des promesses). 

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

Donc, comme on peut le supposer, le code avant ne fonctionne pas vraiment ... Cette hypothèse est correcte.

Problème

Pour que le filtre fonctionne avec une fonction asynchrone, j'ai vérifié stackoverflow et trouvé le sujet suivant:

Filtrer un tableau avec une fonction qui retourne une promesse

Malheureusement, la réponse choisie est trop complexe et utilise des classes. Cela ne va pas pour moi. Je cherche plutôt une solution plus simple, utilisant des fonctions simples avec une approche fonctionnelle. 

Il existe une solution à la fin, utilisant une carte avec un rappel pour simuler un filtre:

https://stackoverflow.com/a/46842181/1337392

Mais j'espérais corriger ma fonction de filtre, pas pour la remplacer.

Des questions

  • Est-il possible d'avoir une fonction asynchrone dans un filtre?
  • Si non, quel est le remplacement le plus simple que je puisse faire?
17
Flame_Phoenix

Il n’existe aucun moyen d’utiliser un filtre avec une fonction asynchrone (du moins, à ma connaissance) . Le moyen le plus simple d’utiliser un filtre avec une collection de promesses consiste à utiliser Promise.all, puis à appliquer la fonction à resultats . Cela ressemblerait à quelque chose comme ceci:

const results = await Promise.all(your_promises)
const filtered_results = results.filter(res => //do your filtering here)

J'espère que ça aide.

14
mcousillas

Utilisez méthodes Scramjet fromArray/toArray ...

const result = await scramjet.fromArray(arr)
                             .filter(async (item) => somePromiseReturningMethod(item))
                             .toArray();

aussi simple que cela - voici un exemple prêt à copier/coller:

const scramjet = require('../../');

async function myAsyncFilterFunc(data) {
    return new Promise(res => {
        process.nextTick(res.bind(null, data % 2));
    });
}

async function x() {
    const x = await scramjet.fromArray([1,2,3,4,5])
        .filter(async (item) => myAsyncFilterFunc(item))
        .toArray();
    return x;
}

x().then(
    (out) => console.log(out),
    (err) => (console.error(err), process.exit(3)) // eslint-disable-line
);

Disclamer: Je suis l'auteur de scramjet. :)

0
Michał Kapracki