setInterval 时间久了不准问题worker解决方案

其实原理就是浏览器切换到后台了,会降低了这两个的频率,省内存嘛,其实从产品角度,这还是没啥问题的,就是坑了前端被!

Worker 解决方案

搜了下,找到了Worker 方法


但是一看,第一个得传url,这传url在传统的里面还行,这特么vue里面怎么整!

看下传统方案

// time-worker.js

var intervalIds = {};

// 监听message 开始执行定时器或者销毁
self.onmessage = function(e){
    switch(e.data.command){
        case 'interval:start':
            var intervalId = setInterval(function(){
                postMessage({
                    message: 'interval:tick',
                    id: e.data.id
                })
            },e.data.interval);

            postMessage({
                message: 'interval:started',
                id: e.data.id
            });
            intervalIds[e.data.id] = intervalId;
            break;
        case 'timeout:start':
            var timeoutId = setTimeout(function(){
                postMessage({
                    message: 'timeout:tick',
                    id: e.data.id
                })
            },e.data.timeout);

            postMessage({
                message: 'timeout:started',
                id: e.data.id
            });
            intervalIds[e.data.id] = timeoutId;
            break;
        case 'interval:clear': // 销毁
            clearInterval(intervalIds[e.data.id]);

            postMessage({
                message: 'interval:cleared',
                id: e.data.id
            })

            delete intervalIds[e.data.id];
            break;
        case 'timeout:clear': // 销毁
            clearTimeout(intervalIds[e.data.id]);

            postMessage({
                message: 'timeout:cleared',
                id: e.data.id
            })

            delete intervalIds[e.data.id];
            break;
    }
}
// worker.js
var worker = new Worker('./time-worker.js');
var workerTimer = {
    id: 0,
    callbacks: {},
    setInterval: function(cb, interval, context) {
        this.id++;
        var id = this.id;
        this.callbacks[id] = { fn: cb, context: context };
        worker.postMessage({ command: 'interval:start', interval: interval, id: id });
        return id;
    },
    setTimeout: function(cb, timeout, context) {
        this.id++;
        var id = this.id;
        this.callbacks[id] = { fn: cb, context: context };
        worker.postMessage({ command: 'timeout:start', timeout: timeout, id: id });
        return id;
    },

    // 监听worker 里面的定时器发送的message 然后执行回调函数
    onMessage: function(e) {
        switch (e.data.message) {
            case 'interval:tick':
                var callback = this.callbacks[e.data.id];
                if (callback && callback.fn) callback.fn.apply(callback.context);
                break;
            case 'timeout:tick':
                var callback = this.callbacks[e.data.id];
                if (callback && callback.fn) callback.fn.apply(callback.context);
                break;
            case 'interval:cleared':
                delete this.callbacks[e.data.id];
                break;
            case 'timeout:cleared':
                delete this.callbacks[e.data.id];
                break;
        }
    },

    // 往worker里面发送销毁指令
    clearInterval: function(id) {
        worker.postMessage({ command: 'interval:clear', id: id });
    },
    clearTimeout: function(id) {
        worker.postMessage({ command: 'timeout:clear', id: id });
    }
};

worker.onmessage = workerTimer.onMessage.bind(workerTimer);

使用

<!doctype html><html lang="en"><head>
    <meta charset="UTF-8">
    <title>稳定版定时器</title></head><body><div>
    <p>Times:500ms</p>
    <p>
        开始时间(s):<span id="startTime"></span> ---- 结束时间(s):<span id="endTime"></span>
    </p>
    <p id="nums"></p>
    <button id="btn">开始</button></div><script src="worker-timer.js"></script><script>
    var nums = document.getElementById('nums');
    var btn = document.getElementById('btn');
    var startTime = document.getElementById('startTime');
    var endTime = document.getElementById('endTime');
    var number = 1;
    var intervalId = null;

    btn.addEventListener('click',function(e){
        if(e.target.innerHTML == '开始'){
            startTime.innerHTML = new Date().getSeconds();
            intervalId = workerTimer.setInterval(function(){
                nums.innerHTML = ++number;
            },500);
            e.target.innerHTML = '暂停';
        }else{
            endTime.innerHTML = new Date().getSeconds();
            workerTimer.clearInterval(intervalId);
            e.target.innerHTML = '开始'
        }

    },false);</script></body></html>

vue2方案

1、下组件

cnpm i worker-loader -D

2、配置vue.config.js

在chainWebpack 按顺序添加就行

    chainWebpack(config) {
     config.module            .rule('worker')
            .test(/\.worker\.js$/)
            .use('worker-loader')
            .loader('worker-loader')
            .options({
                inline: 'fallback',
                filename: 'workerName.[hash].worker.js',
              })
        config.output.globalObject('this')
        // 解决:worker 热更新问题
        config.module.rule('js').exclude.add(/\.worker\.js$/);
    
    }

这里可能存在部分内容冲突

如我下面这个就冲突了

 // 这个不要复制
  config.when(process.env.NODE_ENV === 'development', config =>
                config.devtool('cheap-source-map')
            )

3、utils下新建  web.worker.js 文件

注意下一定要以.worker.js结尾,因为我们配置文件内使用的这个规则,当然你可以自定义

// web.worker.jsvar intervalIds = {};// 监听message 开始执行定时器或者销毁onmessage = function(e){
    switch(e.data.command){
        case 'interval:start':
            var intervalId = setInterval(function(){
                postMessage({
                    message: 'interval:tick',
                    id: e.data.id                })
            },e.data.interval);

            postMessage({
                message: 'interval:started',
                id: e.data.id            });
            intervalIds[e.data.id] = intervalId;
            break;
        case 'timeout:start':
            var timeoutId = setTimeout(function(){
                postMessage({
                    message: 'timeout:tick',
                    id: e.data.id                })
            },e.data.timeout);

            postMessage({
                message: 'timeout:started',
                id: e.data.id            });
            intervalIds[e.data.id] = timeoutId;
            break;
        case 'interval:clear': // 销毁
            clearInterval(intervalIds[e.data.id]);

            postMessage({
                message: 'interval:cleared',
                id: e.data.id            })

            delete intervalIds[e.data.id];
            break;
        case 'timeout:clear': // 销毁
            clearTimeout(intervalIds[e.data.id]);

            postMessage({
                message: 'timeout:cleared',
                id: e.data.id            })

            delete intervalIds[e.data.id];
            break;
    }}

4、新建workerTimer.js

这个可以随便命名

// workerTimer.jsimport Worker from './web.worker'// 参数1是页面路径,参数2是配置参数let worker = new Worker();console.log(Worker)let workerTimer = {
    id: 0,
    callbacks: {},
    setInterval: function (cb, interval, context) {
        this.id++;
        var id = this.id;
        this.callbacks[id] = { fn: cb, context: context };
        worker.postMessage({ command: 'interval:start', interval: interval, id: id });
        return id;
    },
    setTimeout: function (cb, timeout, context) {
        this.id++;
        var id = this.id;
        this.callbacks[id] = { fn: cb, context: context };
        worker.postMessage({ command: 'timeout:start', timeout: timeout, id: id });
        return id;
    },
    // 监听worker 里面的定时器发送的message 然后执行回调函数
    onMessage: function (e) {
        switch (e.data.message) {
            case 'interval:tick':
                var callback = this.callbacks[e.data.id];
                if (callback && callback.fn) callback.fn.apply(callback.context);
                break;
            case 'timeout:tick':
                var callback = this.callbacks[e.data.id];
                if (callback && callback.fn) callback.fn.apply(callback.context);
                break;
            case 'interval:cleared':
                delete this.callbacks[e.data.id];
                break;
            case 'timeout:cleared':
                delete this.callbacks[e.data.id];
                break;
        }
    },

    // 往worker里面发送销毁指令
    clearInterval: function (id) {
        worker.postMessage({ command: 'interval:clear', id: id });
    },
    clearTimeout: function (id) {
        worker.postMessage({ command: 'timeout:clear', id: id });
    }};worker.onmessage = workerTimer.onMessage.bind(workerTimer);export default workerTimer

5、使用方法

<template>
  <div id="app"></div>
</template>

<script>

import workerTimer from "./utils/workerTimer";
console.log(workerTimer);
export default {
  name: "App",
  components: {},
  data() {
    return {};
  },
  created() {
    this.init();
  },
  methods: {

    init() {
      let index = 0;
      let time = Date.parse(new Date());
      const that = this;
      let intervalId
     intervalId = workerTimer.setInterval(function () {
        let newtime = Date.parse(new Date());
        index++;
        console.log("当前执行次数" + index);
        console.log("当前时间秒:" + (newtime - time) / 1000);
        if(index>10){
          workerTimer.clearInterval(intervalId)
        }
      }, 1000);
    },
  },
};
</script>



相关内容

发表评论

验证码:
点击我更换图片

最新评论