Skip to content

Instantly share code, notes, and snippets.

@paullewis
Last active June 11, 2024 21:10
Show Gist options
  • Save paullewis/55efe5d6f05434a96c36 to your computer and use it in GitHub Desktop.
Save paullewis/55efe5d6f05434a96c36 to your computer and use it in GitHub Desktop.
Shims rIC in case a browser doesn't support it.
/*!
* Copyright 2015 Google Inc. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
/*
* @see https://developers.google.com/web/updates/2015/08/using-requestidlecallback
*/
window.requestIdleCallback = window.requestIdleCallback ||
function (cb) {
return setTimeout(function () {
var start = Date.now();
cb({
didTimeout: false,
timeRemaining: function () {
return Math.max(0, 50 - (Date.now() - start));
}
});
}, 1);
}
window.cancelIdleCallback = window.cancelIdleCallback ||
function (id) {
clearTimeout(id);
}
@paullewis
Copy link
Author

@igrigorik Only supported as of iOS 9, which is just a touch too soon for my tastes on a shim. Bit of a shame, really.

@jcubic
Copy link

jcubic commented Dec 12, 2015

As this article says requestIdleCallback accept timeout option.

Copy link

ghost commented Mar 9, 2016

@getify rAF might still be better if the work to be done in the rIC happens to fit within a frame. Maybe using raf-timeout (an idea I'm experimenting with) with this rIC shim would help?

Copy link

ghost commented Mar 9, 2016

@paullewis I stumbled on requestIdleCallback because I'm working on a 3D engine at infamous.io. I'd like to be able to determine if there is time left in an rAF, and thought maybe rIC could be a way to put off some less-important rendering tasks. For example, maybe there's a primary animation, and a secondary animation, where the secondary animation can run at a slower fps if necessary, in order for the primary animation to be at 60 fps. If we call requestIdleCallback(..., {timeout:0}), will 0 for the timeout guarantee that the rIC fires after the next rAF, or can it skip an rAF even if the timeout is 0? This would be happening while the user is interacting; I'd still want to execute the logic after each frame, but I'd like to know if there's time left in the frame and rAF doesn't seem to give us that info.

Basically what I'm looking for is something like if (timeRemainingInRAF > 0) doMoreStuff(), or in english "if there's time remaining within the 16ms window of a 60fps loop...", and it seems like rIC is the only way to achieve this (although calculations in the rIC would be for the following rAF, which is fine). If there's some way to guarantee that we can fire an rIC with every rAF, then that would probably serve the purpose.

What exactly is deadline.timeRemaining? Is that the time remaining until the next vsync? The next rAF? The next what? The 50ms max timeRemaining hints that rIC is not necessarily the timeRemaining before the next rAF, which might mean rIC won't help me out.

The requestAnimationFrame API seems like it leaves me blind: I don't know how much time I have, therefore I don't know if my rendering calculations are taking too long, and I don't know how long they should take. The only thing I know is that the time I have for my rendering calculations is less than 16ms because the browser has to do it's own rendering stuff within those 16ms too.

@Krinkle
Copy link

Krinkle commented Mar 11, 2016

@paullewis Shouldn't start be set when the setTimeout callback runs rather than when it is scheduled?

 window.requestIdleCallback = window.requestIdleCallback ||
   function (cb) {
-    var start = Date.now();
     return setTimeout(function () {
+      var start = Date.now();
       cb({ 
         didTimeout: false,
         timeRemaining: function () {
           return Math.max(0, 50 - (Date.now() - start));
         }
       });
     }, 1);

See also wikimedia/mediawiki@55fc2a9#mediawiki.requestIdleCallback.js.

@chestozo
Copy link

chestozo commented Jan 20, 2017

@Krinkle it your version Date.now() inside cb will be the same as start.
Whereas here you have access to the start time before any idle callbacks were executed.

Basically in original version you can kind of limit idle period
whereas in your version you can only check execution time of individual idle callbacks:

https://developers.google.com/web/updates/2015/08/using-requestidlecallback

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment