DEV Community

Cover image for How to process heavy workloads AND animate at 60fps in Vanilla JS, any Framework or React Native:
Mike Talbot ⭐
Mike Talbot ⭐

Posted on

How to process heavy workloads AND animate at 60fps in Vanilla JS, any Framework or React Native:

TL;DR

  • You can stop glitches in your animations when you are running Javascript code, without waiting for your animation to end
  • I've written a library that lets you easily run processes in the spare time between each frame of your animations
  • It works on the Browser and now tested working in React Native
  • You can stringify, parse, compress, sort, filter, reduce and transform to your hearts content and animations remain smooth
  • You can use it with async functions or dive into using generator functions if you need to break up your own complex calculations
  • It's available on MIT license from my GitHub and as npm -i js-coroutines
  • There's a detailed article on how it works internally available here

How to use js-coroutines to keep your animations smooth:

If you are just sorting, compressing or using array operations you can get away with importing the xxxxAsync methods and using them inside a normal Javascript async function:

import {sortAsync, parseAsync, filterAsync} from 'js-coroutines'

...

async doMyProcess(input) {
    const records = await parseAsync(input)
    const filtered = await filterAsync(records, r=>r.age < 30)
    return await sortAsync(filtered, item=>item.surname)
}

Enter fullscreen mode Exit fullscreen mode

If you have your own complicated process you can use generator functions and run:

import {run, parseAsync} from 'js-coroutines'


async doMyProcess(input) {
  const records = await parseAsync(input)
  return await run(function*() { return yield * myComplicatedProcess(records) })

  function * myComplicatedProcess(data) {
     let total = 0
     for(let i = 0; i < data.items.length - 1; i++) {
       if(i % 100 === 0) yield // Check how much time is available
       if(typeof data.items[i] === 'number') {
          total += (data.items[i+1] - data.items[i]) ** 2
       } else if (typeof data.items[i] === 'object') {
          total += Math.sqrt(yield * myComplicatedProcess(data.items[i]))
       }
     }
     return total
   }
}

Enter fullscreen mode Exit fullscreen mode

Demo

Animations Too!

js-coroutines also allows you to write imperative code for animations

More on that here:

Top comments (2)

Collapse
 
gregfletcher profile image
Greg Fletcher

Very cool!

I've had a quick peek at the source code. Do you exclusively use requestAnimationFrame for the animations? I noticed you also have setTimeOut as well but I wasn't sure if you use that as well.

Also what are your thoughts on using a service worker for off the main thread animations?

Collapse
 
miketalbot profile image
Mike Talbot ⭐

So the animations use requestAnimationFrame - the key thing being to use generators to create imperative for next loops in that case.

The processing stuff uses requestIdleCallback. I polyfill that in a variety of ways.

The problem with other threads is the effort of moving things to and from them, so if the animation was basically a ton of processing that resulted in something simple and small enough to move then it would work. I'm thinking some kind of complex particle simulation or 3d work.