How to implement lazy evaluation in JavaScript

Lazy evaluation defers computation until values are actually needed, reducing memory usage and improving performance for large datasets. As the creator of CoreUI with 25 years of JavaScript optimization experience, I’ve used lazy evaluation to process massive datasets without loading everything into memory.

The most effective approach uses JavaScript generators to create lazy iterables that compute values on demand.

Direct Answer

Use generators for lazy evaluation:

function* lazyRange(start, end) {
  for (let i = start; i <= end; i++) {
    yield i
  }
}

const numbers = lazyRange(1, 1000000)
console.log(numbers.next().value) // 1 (only computes first value)

Lazy Map

function* lazyMap(iterable, fn) {
  for (const item of iterable) {
    yield fn(item)
  }
}

const numbers = lazyRange(1, 1000)
const doubled = lazyMap(numbers, x => x * 2)

for (const num of doubled) {
  if (num > 10) break // Only computes up to 10
  console.log(num)
}

Lazy Filter

function* lazyFilter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item
    }
  }
}

const numbers = lazyRange(1, 1000000)
const evens = lazyFilter(numbers, x => x % 2 === 0)
const first10Evens = lazyFilter(evens, (x, i) => i < 10)

Chain Lazy Operations

function* lazyRange(start, end) {
  for (let i = start; i <= end; i++) {
    yield i
  }
}

function* lazyMap(iterable, fn) {
  for (const item of iterable) {
    yield fn(item)
  }
}

function* lazyFilter(iterable, predicate) {
  for (const item of iterable) {
    if (predicate(item)) {
      yield item
    }
  }
}

function* lazyTake(iterable, count) {
  let taken = 0
  for (const item of iterable) {
    if (taken >= count) break
    yield item
    taken++
  }
}

const result = lazyTake(
  lazyMap(
    lazyFilter(
      lazyRange(1, 1000000),
      x => x % 2 === 0
    ),
    x => x * 2
  ),
  5
)

console.log([...result]) // [4, 8, 12, 16, 20]

Lazy Object Properties

const createLazyObject = (props) => {
  const cache = {}

  return new Proxy({}, {
    get(target, prop) {
      if (prop in cache) {
        return cache[prop]
      }

      if (prop in props) {
        cache[prop] = props[prop]()
        return cache[prop]
      }

      return undefined
    }
  })
}

const user = createLazyObject({
  name: () => 'John Doe',
  posts: () => {
    console.log('Fetching posts...')
    return ['Post 1', 'Post 2']
  }
})

console.log(user.name) // No log
console.log(user.posts) // Logs: Fetching posts...
console.log(user.posts) // No log (cached)

Infinite Sequences

function* fibonacci() {
  let [a, b] = [0, 1]
  while (true) {
    yield a
    ;[a, b] = [b, a + b]
  }
}

const fibs = fibonacci()
console.log(fibs.next().value) // 0
console.log(fibs.next().value) // 1
console.log(fibs.next().value) // 1
console.log(fibs.next().value) // 2

Best Practice Note

This is the same lazy evaluation pattern we use in CoreUI for processing large data tables without loading everything into memory. Generators provide true lazy evaluation in JavaScript, computing values only when needed. This is essential for infinite sequences, large datasets, or expensive computations.

For related performance patterns, check out how to create a memoization function in JavaScript and how to optimize loop performance in JavaScript.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author

Subscribe to our newsletter
Get early information about new products, product updates and blog posts.
What is Double Question Mark in JavaScript?
What is Double Question Mark in JavaScript?

How to force a React component to re-render
How to force a React component to re-render

How to Manage Date and Time in Specific Timezones Using JavaScript
How to Manage Date and Time in Specific Timezones Using JavaScript

How to Add a Tab in HTML
How to Add a Tab in HTML

Answers by CoreUI Core Team