What is raf-schd?
The raf-schd package is a small utility that creates a throttled function that only invokes the provided function at most once per animation frame. It's useful for optimizing performance by reducing the number of times a function is called during rapid events such as scrolling, resizing, or animations.
What are raf-schd's main functionalities?
Throttling function calls
This code sample demonstrates how to create a throttled version of an expensive function that is called at most once per animation frame. This is useful for attaching to events that fire rapidly, such as 'resize' or 'scroll' events.
import schedule from 'raf-schd';
const expensiveFunction = () => {
// Expensive operation
};
const throttledFunction = schedule(expensiveFunction);
window.addEventListener('resize', throttledFunction);
window.addEventListener('scroll', throttledFunction);
Other packages similar to raf-schd
throttle-debounce
The throttle-debounce package provides throttle and debounce functions that can be used to rate-limit functions. It is similar to raf-schd in that it helps in optimizing function execution frequency, but it does not specifically tie the throttling to requestAnimationFrame.
lodash.throttle
lodash.throttle is a function from the popular Lodash library that throttles a function call. It is similar to raf-schd but offers more customization options, such as leading and trailing invocation control. Unlike raf-schd, it does not use requestAnimationFrame and allows setting a custom delay.
frame-throttle
frame-throttle is a package that provides a similar functionality to raf-schd by throttling function calls to the next animation frame. It is a direct alternative to raf-schd, with a similar API and use case.
raf-schd
A scheduler based on requestAnimationFrame
. It throttles calls to a function and only invokes it with the latest argument in the frame period.
import rafScheduler from 'raf-schd';
const expensiveFn = (arg) => {
console.log(arg);
}
const schedule = rafScheduler(expensiveFn);
schedule('foo');
schedule('bar');
schedule('baz');
Why?
raf-schd
supports the use case where you only want to perform an action in an animation frame with the latest value. This an extremely useful performance optmisation.
Without raf-schd
Optimised scroll listener example taken from MDN
var last_known_scroll_position = 0;
var ticking = false;
function doSomething(scroll_pos) {
}
window.addEventListener('scroll', function(e) {
last_known_scroll_position = window.scrollY;
if (!ticking) {
window.requestAnimationFrame(function() {
doSomething(last_known_scroll_position);
ticking = false;
});
}
ticking = true;
});
With raf-schd
import rafScheduler from 'raf-schd';
function doSomething(scroll_pos) {
}
const scheduled = rafSchedule(doSomething);
window.addEventListener('scroll', function() {
schedule(window.scrollY);
});
Types
rafSchduler
type rafScheduler = (fn: Function) => ResultFn
type ResultFn = (...arg: any[]) => number;
At the top level raf-schd
accepts any function a returns a new ResultFn
(a function that wraps your original function). When executed, the ResultFn
returns a number
. This number is the animation frame id. You can use this frame id to cancel the scheduled frame using cancelAnimationFrame(id)
;
The ResultFn
will execute your function with the latest arguments provided to it on the next animation frame.
Throttled with latest argument
import rafScheduler from 'raf-schd';
const doSomething = () => {...};
const schedule = rafScheduler(doSomething);
schedule(1, 2);
schedule(3, 4);
schedule(5, 6);
Cancelling a frame
const scheduled = rafSchedule(doSomething);
const frameId = schedule('foo');
cancelAnimationFrame(frameId);
Testing your code
If you want to really ensure that your code is working how you intend it to - use raf-stub
to test your animation frame logic.
Installation
yarn add raf-schd
npm install raf-schd --save
Module usage
ES6 module
import rafScheduler from 'raf-schd';
CommonJS
If you are in a CommonJS environment (eg Node), then you will need add .default
to your import:
const rafScheduler = require('raf-schd').default;