JavaScript Array Lazy Evaluation Through Iterator Helper

Introduction

Previously mentioned JavaScript’s Iterator Protocol: What makes JavaScript Iterables Iterable?, and arrays, being based on iterators, naturally benefit from the recently launched iterator helper🔗 for lazy evaluation.

What are the shortcomings of eager evaluation array methods?

For array methods like map, filter, slice, they allow for intuitive chaining of data handling, but they perform eager evaluation, creating a new array with each operation. This is not a big issue for small datasets, but when dealing with large datasets or streams, the repeated creation and copying can become quite costly.

const arr = [1, 2, 3].map(x => x * 2).filter(x => x > 2);
// Both map and filter immediately create new arrays

What are the benefits of using Iterator Helpers for lazy evaluation?

Better efficiency and space utilization

In the new Iterator Helpers proposal, arrays can create an iterator using .values(), which allows values to be accessed lazily as the iterator is consumed. This means that each element is only processed when “consumed,” and intermediate results are not created ahead of time.

const iter = [1, 2, 3, 4, 5].values()
.map(x => x * 2)
.filter(x => x > 5)
console.log([...iter]); // [ 6, 8, 10 ]
// For example, only executed when expanded [...] or using for...of

Each method of Iterator Helpers only returns a new iterator, does not produce intermediate arrays, and does not pre-compute all values, allowing for simple conversion back to an array via toArray🔗 or spread syntax.

Lazy evaluation results in fewer computations

For example, to find the top 5 products with over 50% discount from 100,000 entries:

const topDiscounted = products
.filter(p => p.discount > 0.5) // Iterates through all 100,000 entries
.slice(0, 5); // Then take the top 5

Even though theoretically, computation can stop after finding 5 products that meet the condition, eager evaluation means we still have to iterate through all data. Using the mindset of lazy evaluation can significantly reduce the needed computation:

const topDiscounted = products
.values() // Convert to iterator
.filter(p => p.discount > 0.5) // Filter step by step
.take(5); // Stop once finding the top 5 that meet the condition
console.log([...topDiscounted]);

Conclusion

ComparisonArray MethodsIterator Helpers
Data StructureArray (eager)Iterator (lazy)
Computation MethodCreates intermediate arraysProcesses item by item
Typical UseSmall to medium data processingLarge data or stream processing

Further Reading