How to use async functions with Array.some and every in Javascript
Check collections with Promises
In the first article, we've covered how async/await helps with async commands but it offers little
help when it comes to asynchronously processing collections. In this post, we'll look into the some
and the every
functions that are used for a more
efficient reduce
when the result is a boolean value.
The some
and every
functions
These functions get an iteratee function, just like the filter
, but they return a single true/false, depending on whether the predicate returned
a specific value. In the case of the some
, if any of the predicates returned true, the result will be true. For the every
function, if
any returned false, the result will be false.
const arr = [1, 2, 3];
const someRes = arr.some((i) => {
return i % 2 === 0;
});
console.log(someRes);
// true
const everyRes = arr.every((i) => {
return i < 2;
});
console.log(everyRes);
// false
Async some
/every
Using an async filter
Considering only the result, these functions can be emulated with an async filter
, which is already covered in a previous article how to convert to async.
// sync
const some = (arr, predicate) => arr.filter(predicate).length > 0;
const every = (arr, predicate) => arr.filter(predicate).length === arr.length;
// async
const asyncSome =
async (arr, predicate) => (await asyncFilter(arr, predicate)).length > 0;
const asyncEvery =
async (arr, predicate) => (await asyncFilter(arr, predicate)).length === arr.length;
Short-circuiting
But there is an important difference between the built-in some
/every
functions and the filter
-based implementations.
When there is an element that returns true for a some
, it short-circuits and does not process the remaining elements:
const arr = [1, 2, 3];
const res = arr.some((i) => {
console.log(`Checking ${i}`);
return i % 2 === 0;
});
// Checking 1
// Checking 2
console.log(res);
// true
Similarly, every
stops after the first false result:
const arr = [1, 2, 3];
const res = arr.every((i) => {
console.log(`Checking ${i}`);
return i < 2;
});
// Checking 1
// Checking 2
console.log(res);
// false
Let's see how to code an async version that works in a similar way and does the least amount of work!
Async some
The best solution is to use an async for iteration that returns as soon as it finds a truthy result:
const arr = [1, 2, 3];
const asyncSome = async (arr, predicate) => {
for (let e of arr) {
if (await predicate(e)) return true;
}
return false;
};
const res = await asyncSome(arr, async (i) => {
console.log(`Checking ${i}`);
await sleep(10);
return i % 2 === 0;
});
// Checking 1
// Checking 2
console.log(res);
// true
For the first element predicate(e)
returns true, it concludes the for-loop.
Async every
The similar structure works for every
, it's just a matter of negating the conditions:
const arr = [1, 2, 3];
const asyncEvery = async (arr, predicate) => {
for (let e of arr) {
if (!await predicate(e)) return false;
}
return true;
};
const res = await asyncEvery(arr, async (i) => {
console.log(`Checking ${i}`);
await sleep(10);
return i < 2;
});
// Checking 1
// Checking 2
console.log(res);
// false
Whenever there is a false value returned by predicate(e)
, the function is ended without checking the other elements.
Parallel processing
The short-circuiting implementation processes the elements sequentially, which is efficient in terms of resource usage, but it might result in longer execution.
For example, if the iteratee sends requests via a network, it might take some time to send them one at a time. On the other hand, while it might result in more requests sent, sending all of them at the same time would be faster.
Conclusion
The some
and the every
functions are easy to approximate with an async filter
but to follow the synchronous version faithfully, an
async for loop is a better choice.