How to use async functions with Array.some and every in Javascript

Check collections with Promises

Author's image
Tamás Sallai
3 mins

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.

March 24, 2020
In this article