155 lines
5.9 KiB
JavaScript
155 lines
5.9 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
/* eslint-disable */
|
||
const common_utils_1 = require("./common-utils");
|
||
const index_1 = require("../const/index");
|
||
/**
|
||
* 定时器,功能:
|
||
* 1. 支持定时执行回调 [1,n] 次,用于常规延迟、定时操作
|
||
* @example
|
||
* // 默认嵌套执行,count=0 无限次
|
||
* timer.run(callback, {delay: 2000});
|
||
* // count=1 等同于 原始setTimeout
|
||
* timer.run(callback, {delay: 2000, count:0});
|
||
* 2. 支持 RAF 执行回调,用于小流渲染,audio音量获取等任务,需要渲染频率稳定,支持页面退后台后,用 setTimeout 接管,最短 1s 执行一次
|
||
* @example
|
||
* // 默认60fps,可以根据单位时长换算,默认开启后台执行
|
||
* timer.run('raf', callback, {fps: 60});
|
||
* // 设置执行次数
|
||
* timer.run('raf', callback, {fps: 60, count: 300, backgroundTask: false});
|
||
* 3. 支持空闲任务执行回调, requestIdleCallback 在帧渲染的空闲时间执行任务,可以用于 storage 写入等低优先级的任务
|
||
* @example
|
||
* // 支持原生setInterval 但不推荐使用,定时任务推荐用 timeout
|
||
* timer.run('interval', callback, {delay:2000, count:10})
|
||
*/
|
||
class Timer {
|
||
static generateTaskID() {
|
||
return this.currentTaskID++;
|
||
}
|
||
/**
|
||
*
|
||
* @param {string} taskName 'interval' 'timeout'
|
||
* @param {function} callback
|
||
* @param {object} options include:
|
||
* @param {number} options.delay millisecond
|
||
* @param {number} options.count 定时器回调执行次数,0 无限次 or n次
|
||
* @param {boolean} options.backgroundTask 在页面静默后是否继续执行定时器
|
||
*/
|
||
static run(taskName = index_1.NAME.TIMEOUT, callback, options) {
|
||
// default options
|
||
if (taskName === index_1.NAME.INTERVAL) {
|
||
options = Object.assign({ delay: 2000, count: 0, backgroundTask: true }, options);
|
||
}
|
||
else {
|
||
options = Object.assign({ delay: 2000, count: 0, backgroundTask: true }, options);
|
||
}
|
||
// call run(function, {...})
|
||
if ((0, common_utils_1.isPlainObject)(callback)) {
|
||
options = Object.assign(Object.assign({}, options), callback);
|
||
}
|
||
if ((0, common_utils_1.isFunction)(taskName)) {
|
||
callback = taskName;
|
||
taskName = index_1.NAME.TIMEOUT;
|
||
}
|
||
// 1. 创建 taskID,作为 timer task 的唯一标识,在本函数执行完后返回,用于在调用的地方实现互斥逻辑
|
||
// 2. 根据 taskName 执行相应的函数
|
||
const taskItem = Object.assign({ taskID: this.generateTaskID(), loopCount: 0, intervalID: null, timeoutID: null, taskName,
|
||
callback }, options);
|
||
this.taskMap.set(taskItem.taskID, taskItem);
|
||
// console.log(`timer run task:${taskItem.taskName}, task queue size: ${this.taskMap.size}`);
|
||
if (taskName === index_1.NAME.INTERNAL) {
|
||
this.interval(taskItem);
|
||
}
|
||
else {
|
||
this.timeout(taskItem);
|
||
}
|
||
return taskItem.taskID;
|
||
}
|
||
/**
|
||
* 定时循环执行回调函数
|
||
* 可以指定循环的时间间隔
|
||
* 可以指定循环次数
|
||
* @param {object} taskItem
|
||
* @param {function} callback
|
||
* @param {*} delay
|
||
* @param {*} count
|
||
* @returns ID
|
||
*/
|
||
static interval(taskItem) {
|
||
// setInterval 缺点,浏览器退后台会降频,循环执行间隔时间不可靠
|
||
// 创建进入定时器循环的任务函数,函数内:1. 判断是否满足执行条件,2.执行 callback
|
||
// 通过 setInterval 执行任务函数
|
||
// 将 intervalID 记录到 taskMap 对应的 item
|
||
const task = () => {
|
||
taskItem.callback();
|
||
taskItem.loopCount += 1;
|
||
if (this.isBreakLoop(taskItem)) {
|
||
return;
|
||
}
|
||
};
|
||
return taskItem.intervalID = setInterval(task, taskItem.delay);
|
||
}
|
||
/**
|
||
* 延迟执行回调
|
||
* count = 0,循环
|
||
* count = n, 执行n次
|
||
* @param {object} taskItem
|
||
*
|
||
*/
|
||
static timeout(taskItem) {
|
||
// setTimeout 浏览器退后台,延迟变为至少1s
|
||
const task = () => {
|
||
// 执行回调
|
||
taskItem.callback();
|
||
taskItem.loopCount += 1;
|
||
if (this.isBreakLoop(taskItem)) {
|
||
return;
|
||
}
|
||
// 不修正延迟,每次callback间隔平均
|
||
return taskItem.timeoutID = setTimeout(task, taskItem.delay);
|
||
};
|
||
return taskItem.timeoutID = setTimeout(task, taskItem.delay);
|
||
}
|
||
static hasTask(taskID) {
|
||
return this.taskMap.has(taskID);
|
||
}
|
||
static clearTask(taskID) {
|
||
// console.log('timer clearTask start', `| taskID:${taskID} | size:${this.taskMap.size}`);
|
||
if (!this.taskMap.has(taskID)) {
|
||
return true;
|
||
}
|
||
const { intervalID, timeoutID, onVisibilitychange } = this.taskMap.get(taskID);
|
||
if (intervalID) {
|
||
clearInterval(intervalID);
|
||
}
|
||
if (timeoutID) {
|
||
clearTimeout(timeoutID);
|
||
}
|
||
if (onVisibilitychange) {
|
||
document.removeEventListener('visibilitychange', onVisibilitychange);
|
||
}
|
||
this.taskMap.delete(taskID);
|
||
// console.log('timer clearTask end ', `| size:${this.taskMap.size}`);
|
||
return true;
|
||
}
|
||
/**
|
||
* 1. 如果已移除出定时队列,退出当前任务
|
||
* 2. 如果当前任务已满足次数限制,则退出当前任务
|
||
* @param {object} taskItem
|
||
* @returns
|
||
*/
|
||
static isBreakLoop(taskItem) {
|
||
if (!this.taskMap.has(taskItem.taskID)) {
|
||
return true;
|
||
}
|
||
if (taskItem.count !== 0 && taskItem.loopCount >= taskItem.count) {
|
||
this.clearTask(taskItem.taskID);
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
}
|
||
Timer.taskMap = new Map();
|
||
Timer.currentTaskID = 1;
|
||
exports.default = Timer;
|