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

Posted

技术标签:

【中文标题】如何将 Array.prototype.filter 与异步一起使用?【英文标题】:How to use Array.prototype.filter with async? 【发布时间】:2018-04-16 03:23:25 【问题描述】:

背景

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

 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 或 falsefilter 是同步的。在前面的示例中,我没有返回任何一个,我返回一个 Promise (所有异步函数都是 Promises )。

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

所以可以假设,之前的代码并不能真正工作......这个假设是正确的。

问题

为了使过滤器与异步函数一起工作,我检查了 *** 并找到了这个主题:

Filtering an array with a function that returns a promise

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

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

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

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

问题

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

【问题讨论】:

您可以使用现有的异步过滤器功能,例如array-async-filter,或者自己编写。 看起来内置过滤器功能在后台使用Function.call() 来执行您的过滤器功能。这似乎可能会通过不调用 await 来打破 async/await 位 【参考方案1】:

这个答案使用库iter-ops,它处理可迭代对象,并支持异步过滤:

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


附:我是iter-ops的作者。

【讨论】:

【参考方案2】:

改编自 Tamás Sallai 的文章How to use async functions with Array.filter in Javascript,你基本上有 2 个步骤:

    为物体通过创造条件 接收对象,根据条件返回真假

这是一个例子

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

【讨论】:

【参考方案3】:

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

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

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

【讨论】:

【参考方案4】:
    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(...)

【讨论】:

【参考方案5】:

使用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
);

声明:我是超燃冲压发动机的作者。 :)

【讨论】:

【参考方案6】:

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

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

希望对你有帮助。

【讨论】:

我是 Promise 的新手,your_promises 参数是什么?会是我的函数返回一个 promise 对象吗? @OmarRuder 一个可迭代的承诺(例如数组)。

以上是关于如何将 Array.prototype.filter 与异步一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

如何将Ios文件上传到

Qt如何将文字变成图片?

如何将Bitmap保存为本地图片文件?

在MATLAB中如何将图导出

ASP如何将SQLSERVER数据导出到DBF(VF)

如何将CSV格式转换成JSON格式