前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >关于浏览器定时器降频的解决方法

关于浏览器定时器降频的解决方法

作者头像
LIYI
发布2019-09-02 17:41:53
3.1K0
发布2019-09-02 17:41:53
举报
文章被收录于专栏:艺述论专栏艺述论专栏

降频缘由

一个 web socket 链接,为了保持活跃,需要定时向服务器发送心跳信令。该机制通常由 setInterval 实现。

但是,当浏览器 Tab 页处于非激活状态下(浏览器最小化或切向其它 Tab 页)时,基于节能考虑,定时器会处于“休眠”或“降频”状态,在这种情况下,心跳机制就不正常了。

甚至在旧版 windows 系统中,可以看到关于 js 定时器刷新频率的设置:

Tab 页激活状态下,setInterval 最大频率可以达到200多的 fps(每秒触发次数);非激活状态下,只能达到 60 fps。

注:setInterval的回调执行间隔并不是由其第二个参数 delay 决定的。即使在激活状态下,也受限于当前 js 主线程的执行队列是否拥挤。

那么,如何解决这个问题呢?如何使 Tab 页在非激活状态下,尽量保持相对准确的触发呢?

解决方案

解决方法,可以引用这个 js 库:

https://github.com/myonov/momentum

其它代码不变,正常使用 setInterval、setTimeout等定时器方法。

实现原理

翻看momentum的源码,实现原理很简单。主要是通过创建一个看不见的独立线程,在后台运行定时器,并接管默认的定时器方法实现。

1,创建一个定时器容器

代码语言:javascript
复制
var containerFunction = function () {
    var idMap = {};

    self.onmessage = function (e) {
        if (e.data.type === 'setInterval') {
            idMap[e.data.id] = setInterval(function () {
                self.postMessage({
                    type: 'fire',
                    id: e.data.id
                });
            }, e.data.delay);
        } else if (e.data.type === 'clearInterval') {
            clearInterval(idMap[e.data.id]);
            delete idMap[e.data.id];
        } else if (e.data.type === 'setTimeout') {
            idMap[e.data.id] = setTimeout(function () {
                self.postMessage({
                    type: 'fire',
                    id: e.data.id
                });
                // remove reference to this timeout after is finished
                delete idMap[e.data.id];
            }, e.data.delay);
        } else if (e.data.type === 'clearCallback') {
            clearTimeout(idMap[e.data.id]);
            delete idMap[e.data.id];
        }
    };
};

在该容器中,支持 setInterval、clearInterval、setTimeout、clearCallback 四个方法。

2,创建一个独立的后台线程

代码语言:javascript
复制
return new Worker(URL.createObjectURL(new Blob([
    '(',
    containerFunction.toString(),
    ')();'
], {type: 'application/javascript'})));

new Blob构造函数是基于代码字符串,创建一个不可变、原始数据的类文件对象。URL.createObjectURL()静态方法会创建一个 DOMString,其中包含一个表示参数中给出的对象的 URL。new Worker 接受一个 js 脚本的 url,启动一个后台线程。这个后台线程不受浏览 Tab 激活/关闭的影响。

3,接管默认的定时器方法

window对象上默认的全局方法,均可以重写:

代码语言:javascript
复制
window.setInterval = patchedSetInterval;
window.clearInterval = patchedClearInterval;
window.setTimeout = patchedSetTimeout;
window.clearTimeout = patchedClearTimeout;

通过以上方法,项目中其它地方调用 setInterval,真正执行的均是自定义的 patchedSetInterval。以patchedSetInterval、patchedClearInterval为例,具体实现是这样的:

代码语言:javascript
复制
function patchedSetInterval(callback, delay) {
    var intervalId = generateId();

    $momentum.idToCallback[intervalId] = callback;
    $momentum.worker.postMessage({
        type: 'setInterval',
        delay: delay,
        id: intervalId
    });
    return intervalId;
}

function patchedClearInterval(intervalId) {
    $momentum.worker.postMessage({
        type: 'clearInterval',
        id: intervalId
    });

    delete $momentum.idToCallback[intervalId];
}

在注册定时器时,通过 postMessage,向 worker 发送一条消息。独立线程容器的 onmessage内部,监听这条消息:

代码语言:javascript
复制
if (e.data.type === 'setInterval') {
    idMap[e.data.id] = setInterval(function () {
        self.postMessage({
            type: 'fire',
            id: e.data.id
        });
    }, e.data.delay);
}

worker 内部的代码是完全独立的,其内对 setInterval 的调用,是对原生定时器方法的调用,与主线程的接管方法 patchedSetInterval 无关。worker 与 js 主线程只能通过 postMessage、onmessage这样的方式通讯。

在 momentum 主线程代码内,以下代码是对 worker 线程消息的监听:

代码语言:javascript
复制
$momentum.worker.onmessage = function (e) {
    if (e.data.type === 'fire') {
        $momentum.idToCallback[e.data.id]();
    }
};

idToCallback 是外部缓存的回调函数集合,当接收到 fire 指令时,触发对应的回调函数。

使用须知

  1. 使用 momentum,回调函数不支持额外的附加参数。
  2. momentum 并不支持所有的定时器函数,像setImmediate并不支持。
  3. onmessage 并未写错,不是 onMessage,虽然 postMessage 是 postMessage。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-05-01,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 艺述论 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 降频缘由
  • 解决方案
  • 实现原理
    • 1,创建一个定时器容器
      • 2,创建一个独立的后台线程
        • 3,接管默认的定时器方法
        • 使用须知
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档