JS定时器误差:setTimeout和setInterval

引言

项目需要每隔一段时间运行某个方法,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)