How to use Array.prototype.filter with async?

落花浮王杯 提交于 2020-06-07 18:53:52

问题


Background

I am trying to filter an array of objects. Before I filter, I need to convert them to some format, and this operation is asynchronous.

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

So, my first try was to do something like the following using async/await:

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

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

Now, as some of you may know, Array.protoype.filter is a function which callback must return either true or false. filter is synchronous. In the previous example, I am returning none of them, I return a Promise ( all async functions are Promises ).

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

So as one can assume, the code before doesn't really work... That assumption is correct.

Problem

To make filter work with an async function, I checked stackoverflow and found this topic:

Filtering an array with a function that returns a promise

Unfortunately, the chosen answer is overly complex and uses classes. This won't do for me. I am instead looking for a more simple solution, using simple functions with a functional approach.

There is one solution at the very end, using a map with a callback to simulate a filter:

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

But I was hoping to fix my filter function, not to replace it.

Questions

  • Is there a way to have an async function inside a filter?
  • If not, what is the simplest replacement I can do?

回答1:


There is no way to use filter with an async function (at least that I know of). The simplest way that you have to use filter with a collection of promises is to use Promise.all and then apply the function to your collection of results. It would look something like this:

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

Hope it helps.




回答2:


Use Scramjet fromArray/toArray methods...

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

as simple as that - here's a ready example to copy/paste:

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: I am the author of scramjet. :)




回答3:


    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(...)



回答4:


Build a parallel array to your array which you want to call filter on. Await all of the promises from your filter func, in my eg, isValid. In the callback in filter, use the 2nd arg, index, to index into your parallel array to determine if it should be filtered.

// ===============================================
// 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));
})();

This behavior makes sense since any Promise instance has a truthy value, but it's not intuitive at a glance.



来源:https://stackoverflow.com/questions/47095019/how-to-use-array-prototype-filter-with-async

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!