【问题标题】:How to use Array.prototype.filter with async?如何将 Array.prototype.filter 与异步一起使用?
【发布时间】: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

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

问题

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

Filtering an array with a function that returns a promise

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

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

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

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

问题

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

【问题讨论】:

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

标签: javascript arrays filter 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 个步骤:

    1. 为物体通过创造条件
    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 一个可迭代的承诺(例如数组)。
            猜你喜欢
            • 2013-09-11
            • 1970-01-01
            • 2020-09-22
            • 2013-01-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多