DEV Community

Aleksei Berezkin
Aleksei Berezkin

Posted on • Updated on

How to split up CPU-intensive work with async generators

So you have some large amount of work to be done. Perhaps you prepare some heavy API answer, or parse a large document, or compute vertices for your 3d scene. Something like this:

function computeVertices() {
  const vertices = []
  for (let i = 0; i < 10_000_000; i++) {
    vertices.push(computeVertex(i))
  }
  return vertices
}
Enter fullscreen mode Exit fullscreen mode

This code works 200ms, the UI looks unresponsive, scrolls are jumping, and transitions are jarred — all the UX is terrible. Is there a nice way to make pauses during this work? Yes! Async generators to the rescue.

That's how it looks:

async function computeVertices() {
  const workLimiter = createWorkLimiter()
  const vertices = []
  for (let i = 0; i < 10_000_000; i++) {
    await workLimiter.next()
    vertices.push(computeVertex(i))
  }
  return vertices
}
Enter fullscreen mode Exit fullscreen mode

And here's implementation:

async function* createWorkLimiter(
  work = 10,
  pause = 6,
) {
  let start = Date.now()
  for ( ; ; ) {
    yield
    if (Date.now() >= start + work) {
      await delay(pause)
      start = Date.now()
    }
  }
}

function delay(ms) {
  return new Promise(resolve =>
    setTimeout(resolve, ms)
  )
}
Enter fullscreen mode Exit fullscreen mode

Cool, isn't it?

Oldest comments (0)