Wie verwende ich Array.prototype.filter mit async?

Lesezeit: 5 Minuten

Hintergrund

Ich versuche, ein Array von Objekten zu filtern. Bevor ich filtere, muss ich sie in ein bestimmtes Format konvertieren, und dieser Vorgang ist asynchron.

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

Mein erster Versuch bestand also darin, mit async/await so etwas wie das Folgende zu tun:

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

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

Nun, wie einige von Ihnen vielleicht wissen, Array.protoype.filter ist eine Funktion, die Callback muss entweder wahr oder falsch zurückgeben. filter ist synchron. Im vorherigen Beispiel gebe ich keinen von ihnen zurück, ich gebe ein Promise zurück ( alle asynchronen Funktionen sind Promises ).

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

Also wie man annehmen kann, funktioniert der Code davor nicht wirklich… Diese Annahme ist richtig.

Problem

Damit der Filter mit einer asynchronen Funktion funktioniert, habe ich den 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. Ich suche stattdessen nach einer einfacheren Lösung, die einfache Funktionen mit einem funktionalen Ansatz verwendet.

Ganz am Ende gibt es eine Lösung, die eine Karte mit einem Callback verwendet, um einen Filter zu simulieren:

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

Aber ich hatte gehofft, meine Filterfunktion zu reparieren, nicht 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?

  • Sie könnten eine vorhandene asynchrone Filterfunktion wie verwenden Array-Async-Filteroder schreiben Sie Ihre eigene.

    – Matt Browne

    3. November 2017 um 11:49 Uhr

  • Es sieht so aus, als ob die eingebaute Filterfunktion verwendet wird Function.call() unter der Haube, um Ihre Filterfunktion auszuführen. Das scheint wahrscheinlich das async/await-Bit zu unterbrechen, indem es nicht anruft await

    – Paulus

    3. November 2017 um 11:54 Uhr

Es gibt keine Möglichkeit, Filter mit einer asynchronen Funktion zu verwenden (zumindest die mir bekannte). Der einfachste Weg, Filter mit einer Sammlung von Promises zu verwenden, ist use Promise.all und wenden Sie dann die Funktion auf Ihre Ergebnissammlung an. Es würde in etwa so aussehen:

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

Ich hoffe es hilft.

  • Ich bin neu bei Promises, was wäre der your_promises-Parameter? Wäre es meine Funktion, die ein Promise-Objekt zurückgibt?

    – Omar Ruder

    27. September 2020 um 8:55 Uhr

  • @OmarRuder Ein Iterable (z. B. Array) von Versprechungen.

    – Uneingeschränkt

    21. August 2021 um 20:15 Uhr

Benutzeravatar von ce-loco
ce-lok

Angepasst aus dem Artikel So verwenden Sie asynchrone Funktionen mit Array.filter in Javascript von Tamás Sallai haben Sie grundsätzlich 2 Schritte:

  1. Eine, die die Bedingungen für das Passieren eines Objekts schafft
  2. Einer, der die Objekte empfängt und je nach Bedingungen wahr oder falsch zurückgibt

Hier ist ein Beispiel

const arr = [1, 2, 3, 4, 5];

function sleep(ms) {
      return new Promise((resolve) => setTimeout(resolve, ms));
    }

const asyncFilter = async (arr, predicate) => {
    const results = await Promise.all(arr.map(predicate));

    return arr.filter((_v, index) => results[index]);
}

const asyncRes = await asyncFilter(arr, async (i) => {
    await sleep(10);
    return i % 2 === 0;
});

console.log(asyncRes);
// 2,4

Verwenden Scramjet fromArray/toArray Methoden…

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

So einfach ist das – hier ist ein fertiges 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
);

Haftungsausschluss: Ich bin der Autor von scramjet. 🙂

Benutzeravatar von James T
Jakob T.

Erstellen Sie ein paralleles Array zu Ihrem Array, für das Sie den Filter aufrufen möchten. Erwarten Sie alle Versprechungen von Ihrer Filterfunktion, in meinem Beispiel, isValid. Beim Rückruf rein filterverwenden Sie das zweite Argument, index, um in Ihr paralleles Array zu indizieren, um zu bestimmen, ob es gefiltert werden soll.

// ===============================================
// common
// ===============================================
const isValid = async (value) => value >= 0.5;
const values = [0.2, 0.3, 0.4, 0.5, 0.6];


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


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

  console.log(JSON.stringify(filtered2));
})();

Dieses Verhalten macht seit jeher Sinn Promise Beispiel hat einen Wahrheitswert, ist aber nicht auf den ersten Blick intuitiv.

Diese Antwort verwendet die Bibliothek iter-opsdas iterierbare Objekte verarbeitet und asynchrone Filterung unterstützt:

import {pipe, filter, toAsync} from 'iter-ops';

// your input data:
const objs = [{id: 1, data: 'hello'}, {id: 2, data: 'world'}];

const i = pipe(
    toAsync(objs), // make it an async iterable
    filter(async (value) => {
        await convert(); // any async function
        return value.data === 'hello'; // filtering logic
    })
);

for await(const a of i) {
    console.log(a); // filtered data
}

PS Ich bin der Autor von iter-ops.

    Array.prototype.asyncFilter =function( filterFn) {
       const arr = this;
       return new Promise(function(resolve){
         const booleanArr = [];
         arr.forEach(function (e) { 
            booleanArr.push(filterFn(e))
         })
         Promise.all(booleanArr).then(function (booleanArr) {
           const arr2 = arr.filter(function (e, i) {
             return booleanArr[i]
           })
           resolve(arr2)
         })
       })
    }
/** use it like this**/
const arr=[1,2,3]
arr.asyncFilter(async e=>{}).then(...)

Benutzeravatar von Amr
Amr

asyncFilter als Erweiterung zu Array hinzufügen:

@available(macOS 10.15.0, *)
extension Array where Element: Any {
    
    public func asyncFilter(closure: (Element) async -> Bool) async -> Array {
        var result = [Element]()
        for item in self {
            if await closure(item) {
                result.append(item)
            }
        }
        return result
    }
}

Verwendungszweck:

result = await result.asyncFilter { item in
    if <item match> {
        return true
    }
}

1403430cookie-checkWie verwende ich Array.prototype.filter mit async?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy