具有动态持续时间的 setTimeout 函数

setTimeout Function with Dynamic Duration

本文关键字:setTimeout 函数 持续时间 动态      更新时间:2023-09-26

我有一个迭代值的setTimeout函数:

var content = ['one', 'two', 'three', 'four', 'five']
var duration = ['10000', '10000', '20000', '10000', '30000']
for (i = 0; i < content.length; i++) {
    setTimeout(function () {
        alert("hi")
    }, duration[i])
}

但是,持续时间不是动态的。我也尝试过parseInt(duration[i])但这也不起作用。我在这里做错了什么?

-------------澄清-----------------:

据我了解,setTimeout() 方法"在指定的毫秒数后调用函数或计算表达式"1。正如上面的抽象代码非常清楚地表明的那样(尽管可能需要不止一眼才能确定),对于content数组中的每个项目,都有一组要执行的操作。每个操作都与当前操作应等待到下一个操作开始的持续时间(以毫秒为单位)相关联。

在上面的示例中,在启动函数时,初始等待持续时间 10000 毫秒(持续时间数组中的第一项)似乎按预期调用。但是,后续操作不会等待持续时间数组中它们各自值指定的持续时间。

正如另一张海报所提到的,您可能希望每次超时都从最后一个超时的末尾开始。您可以使用建议的"递归"方法,但承诺会产生更具可读性的代码。首先,我们将编写一个wait例程,该例程返回一个在几毫秒内解析的承诺:

function wait(n) {
    return new Promise(function(resolve) { setTimeout( resolve, n); });
}

现在我们可以重写您的循环,如下所示:

var promise = Promise.resolve();
for (i = 0; i < content.length; i++) {
    promise = promise . then(function() { return wait(duration[i]; });
}

我们需要Promise.resolve()以已经解决的承诺开始事情。顺便说一下,作为奖励,在这个循环结束时,你有一个关于整个超时链已经完成的承诺,你可以用它来做下一步的事情。

事实证明,这与

duration.reduce(
    function(promise, ms) {
        return promise . then(function() { return wait(ms); });
    }, Promise.resolve()
);

如果你不知道reduce,这是值得学习的。

但是想象一下,跳过所有这些,只是有一些东西,而不是设置超时,而是在那里等待,那该有多好。事实证明,您实际上可以使用async函数来执行此操作:

async function wait_series(duration) {
^^^^^
    for (i = 0; i < content.length; i++) {
        await wait(duration[i]);
        ^^^^^
    }
}

异步函数需要使用带有适当标志的适当转译器,例如 babel。

如果你没有或不想使用 Babel,但可以访问生成器,你可以使用 co 这样的库将生成器function* ) 变成异步函数,yield你想等待的地方:

function wait_series(duration) {
    co(function *() {
    ^^
        for (i = 0; i < content.length; i++) {
            yield wait(duration[i]);
            ^^^^^
        }
    });
}

使用异步函数或其基于co的等效函数的优点是,您可以像习惯的那样重新编写常规的旧for循环。它们本质上允许您以同步思维方式编写异步代码。你不再需要考虑承诺(尽管co使用它们在引擎盖下发挥了它的魔力)。

你可能会说,这似乎需要学习很多东西,只是为了编写一系列连续的超时。但实际上,promise和异步函数最终比编写超时更容易,后者调用函数然后设置其他超时。

递归解决方案

如果您更喜欢递归解决方案,这里有一个稍微简化的版本,它实际上也可以工作(通过最后的()第一次调用loop):

function wait_series(duration) {
    var i = duration.length;
    (function loop() {
        if (i--) setTimeout(loop, duration[i]);
    })();
      ^^
}

代码的问题在于 for 循环迭代而不等待 setTimeout 完成。
我建议使用这样的递归

var content = ['one','two','three','four','five'];
var duration = ['10000','10000','20000','10000','30000'];
var i = 0;
var loop = function(){
  setTimeout(function(){
    if(++i < content.length);
      loop();
  },parseInt(duration[i]));
}