@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
JavaScript
/**
* 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 };