How to use Array.prototype.filter with async?
Solution 1:
There is no way to use filter with an async function (at least that I know of).
The simplest way that you have to use filter with a collection of promises is to use Promise.all
and then apply the function to your collection of results.
It would look something like this:
const results = await Promise.all(your_promises)
const filtered_results = results.filter(res => //do your filtering here)
Hope it helps.
Solution 2:
Adapted from the article How to use async functions with Array.filter in Javascript by Tamás Sallai, you basically have 2 steps:
- One that creates the conditions for an object to pass
- One that receives the objects and returns true or false according to conditions
Here's an example
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
Solution 3:
Use Scramjet fromArray/toArray methods...
const result = await scramjet.fromArray(arr)
.filter(async (item) => somePromiseReturningMethod(item))
.toArray();
as simple as that - here's a ready example to copy/paste:
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: I am the author of scramjet. :)
Solution 4:
Build a parallel array to your array which you want to call filter on.
Await all of the promises from your filter func, in my eg, isValid
.
In the callback in filter
, use the 2nd arg, index, to index into your parallel array to determine if it should be filtered.
// ===============================================
// 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));
})();
This behavior makes sense since any Promise
instance has a truthy value, but it's not intuitive at a glance.