如何将 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 或 false。 filter
是同步的。在前面的示例中,我没有返回任何一个,我返回一个 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 与异步一起使用?的主要内容,如果未能解决你的问题,请参考以下文章