17

我想每小时执行一些 JS 代码。但我不能使用

setInterval("javascript function",60*60*1000);

因为我想每隔一小时做一次,我的意思是在 1:00、2:00、3:00 等等。我正在考虑类似的事情

var d;
while(true) {
  d = new Date();
  if ((d.getMinutes() == '00') && (d.getSeconds() == '00')){
    // my code here
  }  
}

但它太慢了,而且效果不好。

谢谢你的任何想法

4

17 回答 17

27

我会找出现在是什么时间,弄清楚距离下一个完整小时还有多长时间,然后等那么久。所以,

function doSomething() {
    var d = new Date(),
        h = new Date(d.getFullYear(), d.getMonth(), d.getDate(), d.getHours() + 1, 0, 0, 0),
        e = h - d;
    if (e > 100) { // some arbitrary time period
        window.setTimeout(doSomething, e);
    }
    // your code
}

检查e > 100只是为了确保您不会setTimeout在 5 毫秒之类的时间内进行操作并陷入疯狂的循环。

于 2012-09-06T22:07:46.657 回答
10

您可以做的是每分钟运行一次间隔,并检查时间是否:00在运行实际代码之前。

于 2012-09-06T22:03:10.340 回答
9

看看这个:

function to_be_executed(){
    ...
    ...
    ...
    makeInterval();
}

function makeInterval(){
    var d = new Date();
    var min = d.getMinutes();
    var sec = d.getSeconds();

    if((min == '00') && (sec == '00'))
        to_be_executed();
    else
        setTimeout(to_be_executed,(60*(60-min)+(60-sec))*1000);
}
于 2012-09-06T22:09:10.877 回答
5

高性能和简答

const runEveryFullHours = (callbackFn) => {
  const Hour = 60 * 60 * 1000;
  const currentDate = new Date();
  const firstCall =  Hour - (currentDate.getMinutes() * 60 + currentDate.getSeconds()) * 1000 - currentDate.getMilliseconds();
  setTimeout(() => {
    callbackFn();
    setInterval(callbackFn, Hour);
  }, firstCall);
};

用法:

runEveryFullHours(() => console.log('Run Every Full Hours.'));
于 2020-12-10T10:08:59.280 回答
4

ES6 版本,适用于 NodeJS 和现代浏览器 - 根据浏览器或服务器时钟以毫秒为单位执行。

功能:

const doSomething = (something) => {
  let running = true
  let nextHour = () => {
    return 3600000 - new Date().getTime() % 3600000
  }
  let nextCall = setTimeout(() => {
    something()
    doSomething(something)
  }, nextHour())
  return {
    next() { return running ? nextHour() : -1 },
    exec() { something() },
    stop() {
      clearTimeout(nextCall)
      running = false
    },
    start() {
      clearTimeout(nextCall)
      nextCall = setTimeout(() => {
        something()
        doSomething(something)
      }, nextHour())
      running = true
    }
  }
}

用法:

// Do something at next full hour and repeat forever
doSomething(() => console.log('Full hour reached!'))

// Do something every full hour & stop it
let obj = doSomething(() => console.log('Will I ever execute? :/'))
obj.next() // Time to next execution in milliseconds
obj.next() / 1000 // Time to next execution in seconds
obj.next() / 1000 / 60 // Time to next execution in minutes
obj.stop() // Stop executing every full hour
obj.start() // Continue executing every hour
obj.exec() // Execute now
于 2019-11-08T13:36:31.993 回答
4

您可以使用类似下面的功能,这很容易适应您的需求。

它计算距离下一个间隔还有多长时间,然后在每次运行脚本后重新运行脚本(除非您将第三个参数设置为 true)

function runOnInterval(interval_in_ms, function_to_run, only_run_once = false){
    setTimeout(()=>{
        function_to_run();
        if (!only_run_once) runOnInterval(...arguments);
    }, interval_in_ms - ((Date.now() - (new Date().getTimezoneOffset() * 6e4)) % interval_in_ms));
}

示例用法:

// Every day at midnight:
runOnInterval(24 * 60 * 60 * 1000, my_function_to_run);
// Every hour on the hour:
runOnInterval(60 * 60 * 1000, my_function_to_run);
// Every minute on the minute:
runOnInterval(60000, my_function_to_run);
// Every 10 seconds on the 10 second mark:
runOnInterval(1e4, ()=>console.log('this will be run every 10 seconds on the 10 second mark'));
于 2019-07-01T00:43:19.343 回答
4

这就是我将如何去做,扩展上一个答案:

function callEveryFullHour(my_function) {

    var now = new Date();
    var nextHour = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours() + 1, 0, 0, 0);
    var difference = nextHour - now;

    return window.setTimeout(function(){

        // run it
        my_function();

        // schedule next run
        callEveryFullHour(my_function);

    }, difference);
}

这样,您可以开始停止任何功能在整小时内运行。

用法:

let my_function = () => { // do something };
let timeout = callEveryHour(my_function);

// my_function will trigger every hour

window.clearTimeout(timeout);

// my_function will no longer trigger on the hour
于 2017-09-29T07:15:13.740 回答
3

您可以通过每次清除和设置间隔来做到这一点。这只是第一次,而不是间隔是一小时,而是一小时减去当前的分钟和秒:

var d = new Date();
var secondsPastHour = d.getMinutes()*60 + d.getSeconds();
var intervalId = setInterval( myFn, 60*60*1000 - secondsPastHour*1000 );

function myFn() {
    // do stuff
    // ...
    clearInterval( intervalId );
    intervalId = setInterval( myFn, 60*60*1000 );
}

唯一的问题是它最终可能会开始漂移......解决方案就是在函数内部做同样的事情,就像你在启动它时所做的那样:

var d = new Date();
var secondsPastHour = d.getMinutes()*60 + d.getSeconds();
var intervalId = setInterval( myFn, 60*60*1000 - secondsPastHour*1000 );

function myFn() {
    // do stuff
    // ...
    clearInterval( intervalId );
    var d = new Date();
    var secondsPastHour = d.getMinutes()*60 + d.getSeconds();
    intervalId = setInterval( myFn, 60*60*1000 - secondsPastHour*1000 );
}

这是每分钟更新一次的概念证明(我不想等一小时来测试我的代码!):http: //jsfiddle.net/dRsua/

于 2012-09-06T22:14:38.057 回答
1

您需要每分钟运行一次 setInterval 函数(或每秒运行一次,具体取决于您希望计时器的精度),并在分钟数为零时执行代码(顺便说一句,get minutes 返回一个介于 0 和 59 之间的数字)。

 function myTimer() {
        var d = new Date()
        if (d.getMinutes() == 0) {
        console.log("full hour");
        }
}

 timer = setInterval(function(){myTimer()},60000)

如果您不希望在确定您已经整小时后每隔一秒/分钟运行一次间隔,您可以简单地触发一个新的每小时间隔并清除初始间隔。

var myHourlyTimer = null;

     function myTimer() {
            var d = new Date()
            if (d.getMinutes() == 0) {
            console.log("full hour");
myHourlyTimer = setInterval(function(){myHourlyTimerFunction()},3600000);
clearInterval(timer)
            }
    }

 timer = setInterval(function(){myTimer()},60000)
于 2012-09-06T22:13:40.460 回答
1

也在寻找这个,根据马克的回应,我写了这个:

function callEveryFullHour() {

    var now = new Date();
    var nextHour = new Date(now.getFullYear(), now.getMonth(), now.getDate(), now.getHours() + 1, 0, 0, 0);
    var difference = nextHour - now;

    window.setTimeout(function(){

        // code goes here

        console.log("It's a full hour!")
        callEveryFullHour();

    }, difference);

}
于 2015-09-18T05:13:28.880 回答
1

建议对 Steffan 提出的方法进行改进,这对我来说效果最好。

功能:

let doSomething = function (interval, task) {
      let running = false
      let nextExec = () => {
        if (interval < 0) interval = 1000
        return interval - (new Date().getTime() % interval)
      }
      let taskwrapper = () => {
        //console.log("do more...")
        task()
      }
      let trigger = () => {
        if (nextCall) clearTimeout(nextCall)
        if (running) {
          nextCall = setTimeout(() => {
            taskwrapper()
            trigger()
          }, nextExec())
        }
      }
      let nextCall = null
      return {
        next() {
          return running ? nextExec() : -1
        },
        exec() {
          taskwrapper()
        },
        stop() {
          if (nextCall) clearTimeout(nextCall)
          running = false
        },
        start() {
          if (!running) {
            running = true
            if (nextCall) clearTimeout(nextCall)
            trigger()
          }
        }
      }
    }

/*instantiate an object doSomething(interval, task) */
let obj = doSomething(5000, () => console.log('You go..!'))

/*schedule You go...! every 5 seconds */
obj.start()

/*stop printing  */
//obj.stop()

/*execute task once */
obj.exec()

于 2020-10-06T14:32:52.630 回答
1

使用 Cron Job 代替纯 js 实现是合适的。

var CronJob = require('cron').CronJob;
var job = new CronJob('* * * * * *', function() {
  console.log('You will see this message every second');
}, "@hourly", true, 'America/Los_Angeles');
job.start();
于 2020-12-09T10:55:44.347 回答
1

如果此脚本在类 Unix 服务器上运行,而不是在浏览器上运行,那么crontab可能是最好的解决方案,而不是在脚本中按小时进行调度。

https://www.geeksforgeeks.org/crontab-in-linux-with-examples/

于 2020-12-10T06:39:42.710 回答
1
  1. 创建工作。
  2. 安排在服务器上运行它。
于 2020-12-09T17:17:36.467 回答
1
let timerId = null

function wrapper (cb,date = new Date()) {
    if(!date.getMinutes() && !date.getSeconds()){
        setInterval(cb,60*60*1000);
        clearInterval(timerId)
    }
}

timerId = setInterval(wrapper,1000,yourCb)

使用一个包装函数,它将每秒执行一次,直到整整一小时,然后调用setInterval(cb,60*60*1000)然后停止执行。

于 2020-12-08T19:00:23.300 回答
0

一种简单的方法是持续运行检查以检测时间何时更改:

var lastProcessedHour = -1;

setInterval(function() {
   var d = new Date();
   var currentHour = d.getHours();
   if (currentHour != lastProcessedHour) {
      // do stuff
      console.log("new hour");

      lastProcessedHour = currentHour;
   }
}, 1000);

如果你像上面那样每秒运行一次,脚本最迟会触发一秒钟进入新的小时。

我认为这种方法既健壮又易于理解,从性能的角度来看,每秒运行一次这个简单的检查应该不是问题。

于 2012-09-06T22:28:02.590 回答
0

基本上和其他许多人一样,但更干净一些。其他没注意的Date().getTime()是每小时上面是3600000的整数倍。

fn();          // call this once to exec on the hour

function fn(now) {

   //############## add this to top of your existing fn ###############
   var to_top = 3600000 - new Date().getTime()%3600000;  // msec to top of hour

   clearTimeout(fn.t);                           // in case called out of turn
   fn.t = setTimeout(fn, to_top, true);

   if( !now ) return;
   //###################################################################

   ...fn stuff...
}
于 2021-12-24T11:39:38.030 回答