美文网首页
JS定时器误差:setTimeout和setInterval

JS定时器误差:setTimeout和setInterval

作者: Franchen | 来源:发表于2017-04-20 18:15 被阅读0次
    引言

    项目需要每隔一段时间运行某个方法,JS提供了两种方式实现定时功能,但我们都知道JS定时其实并不靠谱,现在就简单进行测试。

    • 使用MongoDB作为数据库存放每次调用的时间,每一条记录为 { type: 'setTimeout/setInterval', time: new Date().getTime() }
    • 同时使用setTimeout和setInterval定时向MongoDB写入记录
    • 分别读取它们写入的记录,分析time得出总延迟、平均时间、极差、平均差等值。

    实现代码
    /**
     * 测试timeout和interval的稳定性
     */
    
    var MongoClient = require('mongodb').MongoClient;
    var Server = require('mongodb').Server;
    var CONF_DB = require('../config.js').DB;
    
    var collectionTickTest = null;
    var setIntervalIndex = 0;
    var setTimeoutIndex = 0;
    var interval = 1000;
    
    function runTest() {
      if (!collectionTickTest) return;
      console.info('tickTest start');
      setInterval(function(){
        collectionTickTest.insert({
          type: 'setInterval',
          time: new Date().getTime(),
          index: ++setIntervalIndex
        }, function(err, results) {
          if (err) {
            console.error('insert fail: ' + err, err);
          }
        });
      }, interval);
    
      var timeoutInsert = function() {
        collectionTickTest.insert({
          type: 'setTimeout',
          time: new Date().getTime(),
          index: ++setTimeoutIndex
        }, function(err, results) {
          if (err) {
            console.error('insert fail: ' + err, err);
          }
        })
        setTimeout(timeoutInsert, interval);
      }
      setTimeout(timeoutInsert, interval);
    }
    
    function analysis (result) {
      var count = result.length;
      console.log('count: ' + count);
      // 总差
      var sumDiff = (result[count - 1].time - result[0].time) - count * interval;
      console.log('sumDiff: ' + sumDiff);
      // 平均值
      var average = (result[count - 1].time - result[0].time) / count;
      console.log('average: ' + average);
      // 每个值
      var individual = [];
      // 极差
      var max = interval, min = interval;
      // 方差
      var variance = 0;
      for (var i = 0; i < count -1; i++) {
        individual.push(result[i + 1].time - result[i].time);
        if (individual[i] > max) {
          max = individual[i];
        } else if (individual[i] < min) {
          min = individual[i];
        }
        variance += Math.pow((individual[i] - average), 2);
      }
      console.log('max: ' + max + ', min: ' + min + ', maxDiff: ' + (max -min));
      console.log('variance: ' + (variance / count))
    }
    
    function analysisSetInterval () {
      if (!collectionTickTest) return;
      console.info('tickTest analysis setInterval');
      collectionTickTest.find({
        type: 'setInterval'
      }).toArray(function(err, result){
        if (err) {
          console.error('find setInterval result fail: ' + err, err);
        } else {
          analysis(result);
        }
      });
    }
    
    function analysisSetTimeout () {
      if (!collectionTickTest) return;
      console.info('tickTest analysis setTimeout');
      collectionTickTest.find({
        type: 'setTimeout'
      }).toArray(function(err, result){
        if (err) {
          console.error('find setTimeout result fail: ' + err, err);
        } else {
          analysis(result);
        }
      });
    }
    
    var constr = 'mongodb://' + CONF_DB.user + ':' + CONF_DB.password + '@' + CONF_DB.host + ':' + CONF_DB.port + '/' + CONF_DB.db;
    MongoClient.connect(constr, function(err, con) {
      if (err) {
        console.error('Connect fail: ' + err, err)
      } else {
        var db = con.db(CONF_DB.db);
        if (db) {
          db.authenticate(CONF_DB.user, CONF_DB.password, function(err, result) {
            if (err) {
              console.error('DB user authenticate fail: ' + err, err);
              client.close();
              console.log('Connect closed');
            } else {
              db.collection('tickTest', {}, function(err, collection) {
                if (err) {
                  console.error('Access collection tickTest fail: ' + err, err);
                  client.close();
                  console.log('Connect closed');
                } else {
                  collectionTickTest = collection;
                  // runTest();
                  // analysisSetInterval();
                  analysisSetTimeout();
                }
              })
            }
          })
        } else {
          console.error('Cannot access db gtip')
        }
      }
    });
    

    结果分析

    一、1秒测试

    tickTest analysis setInterval
    count: 26875
    sumDiff: 32638
    average: 1001.2144372093023
    max: 1174, min: 998, maxDiff: 176
    variance: 4.110766477637712
    
    tickTest analysis setTimeout
    count: 26875
    sumDiff: 32629
    average: 1001.2141023255814
    max: 1174, min: 999, maxDiff: 175
    variance: 4.1382837776008845
    
    • 在服务器上运行了7个半小时左右,总次数为26875 * 1秒
    • 总延迟都将近半分钟,有32秒多
    • 平均值看起来挺接近1000的,但次数多了还是有影响,特别是需要跟时间同步的时候。
    • 极差主要表现为延迟,最大的延迟达到174ms
    • 方差则表示稳定程度,两者都差不多

    二、1分钟测试

    tickTest analysis setInterval
    count: 241
    sumDiff: -59229
    average: 59754.236514522825
    max: 60028, min: 59999, maxDiff: 29
    variance: 61738.17462713403
    
    tickTest analysis setInterval
    count: 241
    sumDiff: -59229
    average: 59754.236514522825
    max: 60028, min: 59999, maxDiff: 29
    variance: 61738.17462713403
    
    • 在服务器上运行了6个小时,60000 * 241
    • 因为代码是延迟一个Interval才写入第一次,所以计算出来的值少了1分钟
    • 以分钟级的形式进行调用稳定性比以秒级高,总延迟在1秒内,极差也可接受

    相关文章

      网友评论

          本文标题:JS定时器误差:setTimeout和setInterval

          本文链接:https://www.haomeiwen.com/subject/udilzttx.html