有一种方法可以配置javascript的setInterval方法来立即执行该方法,然后与计时器一起执行


当前回答

这个例子建立在@Alnitak的回答上,但是使用await Promise在循环循环中进行更细粒度的控制。

比较的例子:

let stillGoing = true;

(function foo() {
    console.log('The quick brown fox did its thing');
    if (stillGoing) setTimeout(foo, 5000);
})();

foo();

在上面的例子中,我们调用foo(),然后它每5秒调用一次自己。

但是如果,在未来的某个时候,我们为了停止循环而将stillGoing设为false,即使在发出停止命令之后,我们仍然会得到一个额外的log行。这是因为在任何给定的时间,在我们将stillGoing设置为false之前,当前迭代将已经创建了一个超时来调用下一次迭代。

如果我们使用await Promise作为延迟机制,那么我们就有机会在调用下一次迭代之前停止循环:

let stillGoing = true;

(async function foo() {
    console.log('The quick brown fox did its thing');
    await new Promise(resolve => setTimeout(resolve, 5000));
    if (stillGoing) foo();
})();

foo();

在第二个示例中,我们首先设置5000ms的延迟,之后检查stillGoing值并决定是否调用另一个递归是合适的。

如果我们在任意点将stillGoing设为false,在我们设置值之后就不会打印出额外的log行。

需要注意的是,这要求函数是异步的,对于给定的使用,这可能是也可能不是一个选项。

其他回答

最简单的方法是自己第一次直接调用函数:

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();

这保证了调用之间至少有一个延迟间隔。如果需要的话,它还使取消循环变得更容易——当达到循环终止条件时,您不需要调用setTimeout。

更好的是,你可以把这一切都包装在一个立即调用的函数表达式中,它创建了函数,然后像上面一样再次调用自己,并自动启动循环:

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

它定义了函数,并开始了整个循环。

如果你可以使用RxJS,有一个叫做timer()的东西:

import { Subscription, timer } from 'rxjs';

const INITIAL_DELAY = 1;
const INTERVAL_DELAY = 10000;
const timerSubscription = timer(INITIAL_DELAY, INTERVAL_DELAY)
  .subscribe(() => {
    this.updateSomething();
  });

// when destroying
timerSubscription.unsubscribe();

如果你需要,这里有一个包装来美化它:

(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的附加参数,但这个包装器没有考虑到这些参数;我认为这些很少使用,但如果你确实需要它们,请记住这一点。

函数的即时异步调用存在一个问题,因为标准的setTimeout/setInterval即使直接将其设置为0,也具有大约几毫秒的最小超时。这是由浏览器特定的工作引起的。

一个具有REAL零延迟的代码示例,可在Chrome, Safari, Opera中工作

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

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

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

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

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

你也可以把_timer传递给foo,如果你想在特定的条件下清除interval (_timer)

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