it-swarm.com.de

Wie verwende ich Array.prototype.filter mit async?

Hintergrund

Ich versuche, ein Array von Objekten zu filtern. Bevor ich filtern kann, muss ich sie in ein bestimmtes Format konvertieren. Diese Operation ist asynchron. 

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

Also war mein erster Versuch, etwas mit async/await zu tun:

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

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

Wie einige von Ihnen vielleicht wissen, ist Array.protoype.filter eine Funktion, deren Rückruf entweder true oder false zurückgeben muss. filter ist synchron. Im vorherigen Beispiel gebe ich keine zurück, ich gebe ein Versprechen zurück (alle asynchronen Funktionen sind Versprechen). 

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

Wie man also annehmen kann, funktioniert der vorhergehende Code nicht wirklich ... Diese Annahme ist richtig.

Problem

Damit der Filter mit einer asynchronen Funktion funktioniert, habe ich stackoverflow überprüft und dieses Thema gefunden:

Filtern eines Arrays mit einer Funktion, die ein Versprechen zurückgibt

Leider ist die gewählte Antwort zu komplex und verwendet Klassen. Das geht nicht für mich. Stattdessen suche ich nach einer einfacheren Lösung, die einfache Funktionen mit funktionalem Ansatz verwendet. 

Ganz am Ende gibt es eine Lösung, eine Map mit Callback zu verwenden, um einen Filter zu simulieren:

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

Aber ich hatte gehofft, meine Filterfunktion zu korrigieren, nicht um sie zu ersetzen.

Fragen

  • Gibt es eine Möglichkeit, eine asynchrone Funktion in einem Filter zu haben?
  • Wenn nicht, was ist der einfachste Ersatz, den ich tun kann?
17
Flame_Phoenix

Es gibt keine Möglichkeit, Filter mit einer asynchronen Funktion zu verwenden (zumindest die ich kenne). Die einfachste Möglichkeit, Filter mit einer Sammlung von Versprechen zu verwenden, besteht darin, Promise.all zu verwenden und dann die Funktion auf Ihre Sammlung von anzuwenden Ergebnisse . Es würde ungefähr so ​​aussehen:

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

Ich hoffe es hilft.

14
mcousillas

Erstellen Sie ein paralleles Array zu Ihrem Array, für das Sie filter aufrufen möchten. Warten Sie auf alle Versprechungen Ihrer Filterfunktion, z. B. in meinem isValid. Verwenden Sie im Rückruf in filter das 2. Argument index, um in Ihr paralleles Array zu indizieren und zu bestimmen, ob es gefiltert werden soll.

// ===============================================
// common
// ===============================================
const isValid = async () => Math.random() > 0.5;
const values = Array(100).fill(42); // array of size 100 with all its values being 42


// ===============================================
// won't filter anything
// ===============================================
const filtered = values.filter(async v => await isValid());
console.log(filtered.length);


// ===============================================
// filters
// ===============================================
(async () => {
  const shouldFilter = await Promise.all(values.map(isValid));
  const filtered2 = values.filter((value, index) => shouldFilter[index]);

  console.log(filtered2.length);
})();

Dieses Verhalten ist sinnvoll, da jede Promise -Instanz einen Wahrheitswert hat, aber auf den ersten Blick nicht intuitiv ist.

0
James T.

Verwenden Sie Scramjet fromArray/toArray-Methoden ...

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

so einfach ist das - ein einfaches Beispiel zum Kopieren/Einfügen:

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: Ich bin der Autor von Scramjet. :)

0