第一次执行setInterval函数而不会延迟


339

有一种方法可以配置setIntervaljavascript方法以立即执行该方法,然后使用计时器执行


4
虽然不是本地的。您可以尝试拨打function一次电话,然后进行setInterval()
Mrchief 2011年

Answers:


519

第一次直接直接调用该函数最简单:

foo();
setInterval(foo, delay);

但是,有充分的理由要避免setInterval-特别是在某些情况下,整个setInterval事件负载可以立即彼此紧接而无延迟地到达。另一个原因是,如果要停止循环,则必须显式调用clearInterval,这意味着必须记住原始setInterval调用返回的句柄。

因此,另一种方法是foo使用以下方法触发自己的后续调用setTimeout

function foo() {
   // do stuff
   // ...

   // and schedule a repeat
   setTimeout(foo, delay);
}

// start the cycle
foo();

这样可以确保两次调用之间至少有间隔delay。如果需要的话,它也使取消循环变得更加容易- setTimeout达到循环终止条件时,您不会调用。

更好的是,您可以将它们全部包装在立即调用的函数表达式中,该表达式创建函数,然后如上所述再次调用自身,并自动启动循环:

(function foo() {
    ...
    setTimeout(foo, delay);
})();

定义功能并一次性开始循环。


5
你为什么喜欢setTimeout
Sanghyun Lee

19
@Sangdol,因为它可以确保计时器事件在未处理的情况下不会“堆叠”。在某些情况下,整个setInterval事件负载可以彼此立即到达而没有任何延迟。
Alnitak

8
应该有某种测试,让您在疲倦时不会在堆栈上发帖
James

3
如果使用该setTimeout方法,如何停止该功能?
Gaurav Bhor,2013年

6
@DaveMunger不,因为它不是真正的递归-只是“伪递归”。在浏览器返回事件循环之前,“递归”调用不会发生,此时,调用堆栈已完全解开。
Alnitak

209

我不确定我是否正确理解了您,但是您可以轻松地执行以下操作:

setInterval(function hello() {
  console.log('world');
  return hello;
}(), 5000);

显然有许多方法可以做到这一点,但这是我能想到的最简洁的方法。


16
这是一个很酷的答案,因为它是一个立即执行并返回自身的命名函数。正是我想要的。
jmort253 2014年

41
绝对是一个很酷的解决方案,但将来可能会引起人们的困惑。
Deepak Joy 2015年

4
您无需给它起一个名字“ hello”。您可以改为返回arguments.callee并使用匿名函数进行修改
JochenJung 2015年

10
@JochenJung arguments.callee在ES5严格模式下不可用
Alnitak

3
这种Javascript代码会使大多数来自其他语言的新JS程序员感到困惑。如果您的公司没有很多JS人员并且您想要工作安全,请编写这样的一堆东西。
伊恩(Ian)

13

由于相同的问题,我偶然发现了这个问题,但是如果您需要表现得完全一样,setInterval()唯一的区别是该函数在开始时立即被调用,那么所有答案都无济于事。

这是我对这个问题的解决方案:

function setIntervalImmediately(func, interval) {
  func();
  return setInterval(func, interval);
}

该解决方案的优势:

  • 现有使用代码setInterval可以容易地通过替代被适配
  • 在严格模式下工作
  • 它与现有的命名函数和闭包一起工作
  • 您仍然可以使用返回值并将其传递给clearInterval()以后

例:

// create 1 second interval with immediate execution
var myInterval = setIntervalImmediately( _ => {
        console.log('hello');
    }, 1000);

// clear interval after 4.5 seconds
setTimeout( _ => {
        clearInterval(myInterval);
    }, 4500);

要厚脸皮,如果你确实需要使用setInterval,那么你也可以替换原来的setInterval。因此,在现有代码之前添加以下代码无需更改代码:

var setIntervalOrig = setInterval;

setInterval = function(func, interval) {
    func();
    return setIntervalOrig(func, interval);
}

尽管如此,上面列出的所有优点仍适用于此,但无需替代。


我更喜欢这种解决setTimeout方案,因为它返回一个setInterval对象。为了避免调用可能稍后在代码中并因此在当前时间未定义的函数,我将第一个函数调用包装在这样的setTimeout函数中:setTimeout(function(){ func();},0);然后在当前处理周期之后调用第一个函数,该处理周期也立即为,但是更多错误证明。
Pensan '16

8

您可以包装setInterval()提供该行为的函数:

function instantGratification( fn, delay ) {
    fn();
    setInterval( fn, delay );
}

...然后像这样使用它:

instantGratification( function() {
    console.log( 'invoked' );
}, 3000);

5
我认为您应该返回setInterval返回的内容。这是因为否则您将无法使用clearInterval。
亨里克R

5

如果需要,这里有一个包装好的包装物:

(function() {
    var originalSetInterval = window.setInterval;

    window.setInterval = function(fn, delay, runImmediately) {
        if(runImmediately) fn();
        return originalSetInterval(fn, delay);
    };
})();

将setInterval的第三个参数设置为true,它将在调用setInterval后立即首次运行:

setInterval(function() { console.log("hello world"); }, 5000, true);

或省略第三个参数,它将保留其原始行为:

setInterval(function() { console.log("hello world"); }, 5000);

一些浏览器支持setInterval的其他参数,该包装器没有考虑这些参数。我认为这些很少使用,但是如果您确实需要它们,请记住这一点。


9
覆盖本机浏览器功能是很糟糕的,因为当规格更改时,它可能破坏其他共存的代码。:其实,setInterval的目前有多个参数developer.mozilla.org/en-US/docs/Web/API/WindowTimers/...
阿克塞尔科斯塔斯·佩纳

2

对于某人,需要将外部this内部带入箭头功能。

(function f() {
    this.emit("...");
    setTimeout(f.bind(this), 1000);
}).bind(this)();

如果以上产生的垃圾困扰您,则可以改为关闭。

(that => {
    (function f() {
        that.emit("...");
        setTimeout(f, 1000);
    })();
})(this);

或者,也许可以考虑使用@autobind装饰取决于你的代码。


1

我建议按以下顺序调用函数

var _timer = setInterval(foo, delay, params);
foo(params)

_timer如果您希望clearInterval(_timer)在特定条件下,也可以将传递给foo

var _timer = setInterval(function() { foo(_timer, params) }, delay);
foo(_timer, params);

您能更深入地解释一下吗?
Polyducks

您可以从第二次通话中设置计时器,第一次是直接直接拨打fn。
Jayant Varshney,

1

对于新手来说,这是一个简单的版本,不会一团糟。它只是声明函数,调用它,然后开始间隔。而已。

//Declare your function here
function My_Function(){
  console.log("foo");
}    

//Call the function first
My_Function();

//Set the interval
var interval = window.setInterval( My_Function, 500 );


1
这就是前几行所接受的答案。这个答案如何添加新内容?
Dan Dascalescu '18

接受的答案继续说不这样做。我对已接受答案的回答说明了为什么它仍然是有效方法。OP专门要求在第一次调用时调用setInterval的函数,但接受的答案转移来谈论setTimeout的好处。
Polyducks

0

为了解决这个问题,我在页面加载后第一次运行该函数。

function foo(){ ... }

window.onload = function() {
   foo();
};

window.setInterval(function()
{
    foo(); 
}, 5000);

0

有一个方便的npm软件包,称为firstInterval(完整披露,这是我的)。

这里的许多示例都不包含参数处理,也没有更改默认行为 setInterval在任何大型项目中都是有害的。从文档:

这个图案

setInterval(callback, 1000, p1, p2);
callback(p1, p2);

等同于

firstInterval(callback, 1000, p1, p2);

如果您是浏览器的老手,又不想依赖,则可以轻松地从代码中剪切和粘贴


0

// YCombinator
function anonymous(fnc) {
  return function() {
    fnc.apply(fnc, arguments);
    return fnc;
  }
}

// Invoking the first time:
setInterval(anonymous(function() {
  console.log("bar");
})(), 4000);

// Not invoking the first time:
setInterval(anonymous(function() {
  console.log("foo");
}), 4000);
// Or simple:
setInterval(function() {
  console.log("baz");
}, 4000);

好的,这是如此复杂,所以,让我说得更简单:

function hello(status ) {    
  console.log('world', ++status.count);
  
  return status;
}

setInterval(hello, 5 * 1000, hello({ count: 0 }));


这是过度设计的。
Polyducks

0

您可以在函数中设置一个很小的初始延迟时间(例如100)并将其设置为所需的延迟时间:

var delay = 100;

function foo() {
  console.log("Change initial delay-time to what you want.");
  delay = 12000;
  setTimeout(foo, delay);
}


-2

立即调用函数存在问题,因为即使将setTimeout / setInterval直接设置为0,标准setTimeout / setInterval的最小超时也只有几毫秒左右。这是由浏览器特定的工作引起的。

零延迟的代码示例可在Chrome,Safari,Opera中使用

function setZeroTimeout(callback) {
var channel = new MessageChannel();
channel.port1.onmessage = callback;
channel.port2.postMessage('');
}

您可以在这里找到更多信息

在第一次手动调用之后,您可以使用函数创建一个间隔。


-10

实际上最快是

interval = setInterval(myFunction(),45000)

这将调用myfunction,然后每45秒执行一次agaian操作,这与实际操作不同

interval = setInterval(myfunction, 45000)

不会调用它,而只安排它


你从哪里得到那个的?
肯·夏普

2
仅当myFunction()返回自身时此方法才起作用。相反,修改每个要调用的函数setInterval是一种更好的方法,setInterval就像其他答案一样,可以包装一次。
詹斯·沃斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.