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

8 5

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:

Image of Datadog

The Essential Toolkit for Front-end Developers

Take a user-centric approach to front-end monitoring that evolves alongside increasingly complex frameworks and single-page applications.

Get The Kit

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.

Image of Docusign

🛠️ Bring your solution into Docusign. Reach over 1.6M customers.

Docusign is now extensible. Overcome challenges with disconnected products and inaccessible data by bringing your solutions into Docusign and publishing to 1.6M customers in the App Center.

Learn more