【问题标题】:How can I filter an array on an asynchronous predicate?如何过滤异步谓词上的数组?
【发布时间】:2020-09-11 15:31:36
【问题描述】:

我有一个 users 数组,我想根据 redis 集中的廉价查找来获取其中的一个子集。

const users = [
  { _id: '1', name: 'george', age: 36 },
  { _id: '2', name: 'henry', age: 33 },
  { _id: '3', name: 'agatha', age: 28 },
  { _id: '4', name: 'janet', age: 29 },
  { _id: '5', name: 'gary', age: 21 },
  // ... 995 more users
]

const db = {/* my redis connection */}

const isInside = (db, user) => {
  return db.contains('my:set:key', user._id)
}

我尝试了Array.prototype.filter,但它似乎不起作用

users.filter(user => isInside(db, user))
// => always gives me back all the users even when I see they are not in the set

我知道这里有问题。如何过滤掉使用isInside的用户?

【问题讨论】:

    标签: javascript arrays filter promise async-await


    【解决方案1】:

    问题在于filter 始终是同步的,而您的数据库调用是异步的,过滤器函数始终返回true,因为它的db.contains 是一个运行承诺,所以它转换为true

    其中一个解决方案可能是,创建一组承诺,等待所有承诺,然后过滤掉。

    const users = [
      { _id: '1', name: 'george', age: 36 },
      { _id: '2', name: 'henry', age: 33 },
      { _id: '3', name: 'agatha', age: 28 },
      { _id: '4', name: 'janet', age: 29 },
      { _id: '5', name: 'gary', age: 21 },
      // ... 995 more users
    ]
    
    const dbCheck = users.map(user => isInside(db, user))
    
    Promise.all(dbCheck).then((values) => {
      // here you have array of bools [true, false ...]
    
      const filteredUsers = users.filter((_, index) => values[index]))
    });
    
    

    【讨论】:

      【解决方案2】:

      您可能对Promise.all(users.map(user => isInside(db, user))) 没问题,但是对于多个同时请求,特别是对于某些第 3 方云服务,存在对数据库造成太大影响的危险。

      如果是这样,那么您可以根据Array.prototype.reduce 编排一个异步过滤器,在该过滤器中按顺序执行数据库查询。

      有点闲聊,但还不错:

      const users = [
      	  { _id: '1', name: 'george', age: 36 },
      	  { _id: '2', name: 'henry', age: 33 },
      	  { _id: '3', name: 'agatha', age: 28 },
      	  { _id: '4', name: 'janet', age: 29 },
      	  { _id: '5', name: 'gary', age: 21 },
      	  // ... 995 more users
      ]
      
      users.reduce(function(promise, user) {
      	return promise
      	.then(arr => {
      		return isInside(null, user) // <<< the asynchronous call
      		.then(bool => { // isInside delivers Boolean
      			if(bool) arr.push(user); // act, depending on asynchronously derived Boolean
      			return arr; // deliver arr to next iteration of the reduction
      		});
      	});
      }, Promise.resolve([])) // starter promise, resolved to empty array
      .then(filtered => {
      	console.log(filtered); // Yay! a filtered array
      });
      
      // dummy isInside() function
      function isInside(db, user) {
      	return Promise.resolve(Math.random() < 0.5); // 50% probability
      }

      当然,这会比.map() 解决方案慢,但如果.map() 不起作用......

      【讨论】:

      • 这是一个异步转换器。感谢您的承诺
      • 可以很容易地改写为Array.prototype上的一种方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-04-07
      • 1970-01-01
      相关资源
      最近更新 更多