如何使用Array.prototype.filter与异步?

时间:2017-11-03 11:45:32

标签: javascript arrays filter async-await

背景

我正在尝试过滤一组对象。在我过滤之前,我需要将它们转换为某种格式,并且此操作是异步的。

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

所以,我的第一次尝试是使用async / await执行类似下面的操作:

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

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

现在,正如你们中的一些人所知,Array.protoype.filter是一个回调必须返回true或false 的函数。 filter是同步的。在前面的例子中,我没有返回它们,我返回一个Promise(所有异步函数都是Promises)。

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

正如人们可以假设的那样,以前的代码并没有真正起作用......这个假设是正确的。

问题

为了使过滤器能够使用异步函数,我检查了stackoverflow并找到了这个主题:

Filtering an array with a function that returns a promise

不幸的是,所选择的答案过于复杂并且使用了类。这不会对我有用。我正在寻找一个更简单的解决方案,使用功能方法的简单功能。

最后有一个解决方案,使用带回调的地图来模拟过滤器:

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

但我希望修复我的过滤功能,而不是替换它。

问题

  • 有没有办法在过滤器中使用异步功能?
  • 如果没有,我能做的最简单的替换是什么?

5 个答案:

答案 0 :(得分:17)

无法使用具有异步功能的过滤器(至少我知道)。 使用带有promise集合的过滤器的最简单方法是使用Promise.all,然后将该函数应用于结果集合。 它看起来像这样:

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

希望它有所帮助。

答案 1 :(得分:1)

使用Scramjet fromArray / toArray方法......

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

就这么简单 - 这是一个复制/粘贴的现成例子:

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:我是scramjet的作者。 :)

答案 2 :(得分:0)

为要在其上调用filter的数组构建一个并行数组。 在您的过滤器功能中,例如在isValid中,等待所有的承诺。 在filter中的回调中,使用第二个参数index索引到并行数组中,以确定是否应对其进行过滤。

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

这种行为是有道理的,因为任何Promise实例都具有真实值,但乍看之下并不直观。

答案 3 :(得分:0)

    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 :(得分:0)

我只能说这个人TamásSallai很好地解释了这一点:基本上,您执行2个功能:

  1. 为物体通过创造条件的人
  2. 一个接收对象并根据条件返回true或false的人

文章:https://advancedweb.hu/how-to-use-async-functions-with-array-filter-in-javascript/