UNPKG

@livelybone/request-idle-callback

Version:

A polyfill for `window.requestIdleCallback`, support NodeJs. It can be used for time slicing

145 lines (122 loc) 3.51 kB
/** * Bundle of @livelybone/request-idle-callback * Generated: 2020-05-11 * Version: 1.1.0 * License: MIT * Author: 2631541504@qq.com */ import FrameDuration from 'frame-duration'; var DocVisibility = { hidden: false }; function listenVisibilityChange(cb) { if (typeof document !== 'undefined') { var hiddenProp = 'hidden' in document ? 'hidden' : 'webkitHidden' in document ? 'webkitHidden' : 'mozHidden' in document ? 'mozHidden' : null; if (hiddenProp) { var visibilityChangeEventName = hiddenProp.replace(/hidden/i, 'visibilitychange'); window.addEventListener(visibilityChangeEventName, function () { // @ts-ignore DocVisibility.hidden = document[hiddenProp]; cb(DocVisibility.hidden); }); } } } FrameDuration.duration = 1000 / 60; var now = function now() { return new Date().getTime(); }; var endTimeOfLastFrame = now(); function _timeRemaining() { return Math.max(0, FrameDuration.duration - (now() - endTimeOfLastFrame)); } function createIdleDeadline(didTimeout) { return { didTimeout: didTimeout, timeRemaining: function timeRemaining() { return _timeRemaining() * 50 / FrameDuration.duration; } }; } var handleId; function runInIdleTimeOfFrame(queue) { cancelAnimationFrame(handleId); var run = function run() { // Run immediate after other raf callback in one frame setTimeout(function () { var i = 0; for (; _timeRemaining() && i < queue.length; i += 1) { var task = queue[i]; if (!task.canceled && !task.done) { try { task.callback(createIdleDeadline(false)); } catch (e) { // prettier-ignore setTimeout(function () { throw e; }, 0); } task.done = true; } } // Update the task queue. this is not pure queue.splice(0, i); endTimeOfLastFrame = now(); // Run the rest of tasks in next loop if (queue.length) runInIdleTimeOfFrame(queue); }); }; if (!DocVisibility.hidden) { // Run in idle time of next frame handleId = requestAnimationFrame(run); } else { // Run immediately when the doc is hidden, // because the requestAnimationFrame will be blocked when the doc is hidden run(); } } var $id = 0; function createIdleTask(_callback) { return { id: ++$id, canceled: false, done: false, callback: function callback(deadline) { try { _callback(deadline); } catch (e) { // prettier-ignore setTimeout(function () { throw e; }, 0); } } }; } var queue = []; function requestIdleCallback(callback, options) { var task = createIdleTask(callback); queue.push(task); if (options && options.timeout && +options.timeout) { setTimeout(function () { if (!task.done) { task.callback(createIdleDeadline(true)); task.done = true; } }, +options.timeout); } runInIdleTimeOfFrame(queue); return task.id; } function cancelIdleCallback(id) { for (var i = 0; i < queue.length; i += 1) { if (queue[i].id === id) { queue[i].canceled = true; break; } } } // When the doc is hidden, // the requestAnimationFrame which the requestIdleCallback based on will be blocked, // so we should run the queue actively listenVisibilityChange(function (hidden) { if (hidden) runInIdleTimeOfFrame(queue); }); export { cancelIdleCallback, requestIdleCallback };