setTimeout 在 forEach 中不起作用

发布于 2025-01-10 11:55:41 字数 394 浏览 0 评论 0原文

我有一个调用函数的 forEach 。每次调用之间需要有一个延迟。我已将其放在 forEach 内的 setTimeout 内。它不尊重第一次等待后的超时。相反,它等待一次,然后立即运行。我已将超时设置为 5 秒,并使用控制台进行确认。等待 5 秒,然后几个 foobar 控制台同时记录。

为什么我会出现这种行为?

var index = 0;
json.objects.forEach(function(obj) {
    setTimeout(function(){
        console.log('foobar');
        self.insertDesignJsonObject(obj, index);
    }, 5000);
});

I have a forEach that calls a function. There needs to be a delay between each time it is called. I've put it inside a setTimeout inside the forEach. It isn't respecting the timeout after the first wait. Instead it is waiting once, then running all at once. I've set the timeout to 5 seconds and I am using a console to confirm. 5 seconds of wait, then several foobar console logs all at once.

Why am I getting this behavior?

var index = 0;
json.objects.forEach(function(obj) {
    setTimeout(function(){
        console.log('foobar');
        self.insertDesignJsonObject(obj, index);
    }, 5000);
});

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心凉怎暖 2025-01-17 11:55:41

杰森的回答完全正确,但我想我应该尝试一下,以便更好地澄清。

这实际上是一个经典的闭包问题。通常它看起来像这样:

for(var i = 0; i < 10; i++){
    setTimeout(function(){
        console.log(i);
    },i * 1000)
}

新手会期望控制台显示:

0
(0 seconds pass...)
1
(1 second passes...)
2
(etc..)

但事实并非如此!您实际上会看到数字 10 被记录了 10 次(每秒 1 次)!

“为什么会这样?”很好的问题。封闭范围。上面的 for 循环缺少闭包作用域,因为在 javascript 中,只有函数(lambda)具有闭包作用域!

请参阅:https://developer.mozilla.org/en-US/docs /Web/JavaScript/Closures

但是!如果您尝试过以下操作,您的尝试将获得所需的输出:

    json.objects.forEach(function(obj,index,collection) {
        setTimeout(function(){
            console.log('foobar');
            self.insertDesignJsonObject(obj, index);
        }, index * 5000);
    });

因为您可以访问“closur-ed”index变量 - 您可以依赖它的状态作为函数(lambda ) 被调用!

其他资源:

JavaScript 闭包如何工作?

http://javascript.info/tutorial/closures

http://code.tutsplus.com/tutorials/closures-front-返回--net-24869

What Jason said is totally correct in his answer but I thought I would give it a shot, to better clarify.

This is actually a classic closure problem. Typically it would look something like:

for(var i = 0; i < 10; i++){
    setTimeout(function(){
        console.log(i);
    },i * 1000)
}

The novice would expect the console to show:

0
(0 seconds pass...)
1
(1 second passes...)
2
(etc..)

But that just isn't the case! What you would actually see is the number 10 getting logged 10 times (1x per second)!

"Why does that happen?" Great question. Closure scope. The for loop above lacks closure scope because in javascript, only functions (lambdas) have closure scope!

See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures

However! Your attempt would have achieved the desired output if you had tried this:

    json.objects.forEach(function(obj,index,collection) {
        setTimeout(function(){
            console.log('foobar');
            self.insertDesignJsonObject(obj, index);
        }, index * 5000);
    });

Because you have access to the "closur-ed" index variable - you can rely on its state being the expected state when the function (lambda) is invoked!

Other Resources:

How do JavaScript closures work?

http://javascript.info/tutorial/closures

http://code.tutsplus.com/tutorials/closures-front-to-back--net-24869

汹涌人海 2025-01-17 11:55:41

setTimeout 是异步的。它的作用是注册一个回调函数并将其置于后台,该回调函数将在延迟后触发。而 forEach 是同步函数。因此,您的代码所做的是“一次性”注册回调,每个回调将在 5 秒后触发。

有两种方法可以避免它:

有一个索引来设置计时器。

json.objects.forEach(function(obj, index) {
    setTimeout(function(){
      // do whatever
    }, 5000 * (index + 1));
});

这种方式的延迟因素是基于你的对象的索引,所以即使你同时注册它们,它也会根据它们的索引触发自己的延迟。 index + 1 保持与问题相同的结果,因为它从 0 开始。

setInterval 循环对象

var i = 0;
var interval = setInterval(function(){
    var obj = json.objects[i];
    // do whatever
    i++;
    if(i === json.objects.length) clearInterval(interval);
}, 5000);

setInterval 与 setTimeout 类似,尽管它根据时间间隔定期触发。在这里,我们访问对象并更新间隔函数内部的索引。另外不要忘记最后清除间隔。

两者之间的区别是 setInterval 只注册了一个函数,而 setTimeout 则注册了与列表中项目数量一样多的函数。

setTimeout is async. What it does is register a callback function and put it in background which will be triggered after delay. Whereas forEach is synchronous function. So what your code did is register callbacks "all at once" that each will be triggered after 5 seconds.

Two ways to avoid it:

Have an index to set the timer.

json.objects.forEach(function(obj, index) {
    setTimeout(function(){
      // do whatever
    }, 5000 * (index + 1));
});

This way the delay factor is based on the index of your objects, so even you register them at same time, it will trigger based on their own delay. index + 1 to keep the same result as in question since it starts at 0.

setInterval to loop your objects

var i = 0;
var interval = setInterval(function(){
    var obj = json.objects[i];
    // do whatever
    i++;
    if(i === json.objects.length) clearInterval(interval);
}, 5000);

setInterval is similar to setTimeout, although it triggers periodically based on interval. In here we access object and update the index inside of the interval function. Also don't forget to clear interval in the end.

The difference between those two is setInterval only registered one function compare to setTimeout registered as many as number of items in the list.

荭秂 2025-01-17 11:55:41

forEach 同步运行,循环遍历所有元素并在五秒后为每个元素安排一个计时器回调。五秒钟后,所有这些回调都会发生。

在 2022 年,有两种主要方法:

  1. 使用链式 setTimeoutindex 变量。

  2. 使用async函数、await和循环。

这是#1 的示例:

const json = {
    objects: [
        {name: "first object"},
        {name: "second object"},
        {name: "third object"},
        {name: "fourth object"},
        {name: "fifth object"},
    ],
};

function insertDesignJsonObject(obj, index) {
    console.log(obj.name, index);
}

let index = 0; // The index of the next element to show
function doNext() {
    // Do this one, increment the index
    /*self.*/insertDesignJsonObject(json.objects[index], index);
    ++index;

    // If we still have one to do, do it after a delay
    if (index < json.objects.length) {
        setTimeout(doNext, 1000); // 5000 for a five-second delay
    }
}
doNext();

这是#2:

const json = {
    objects: [
        {name: "first object"},
        {name: "second object"},
        {name: "third object"},
        {name: "fourth object"},
        {name: "fifth object"},
    ],
};

function insertDesignJsonObject(obj, index) {
    console.log(obj.name, index);
}

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

(async () => {
    for (let index = 0; index < json.objects.length; ++index) {
        // Wait to do this one until a delay after the last one
        if (index > 0) {
            await delay(1000); // 5000 for five seconds
        }

        // Do this one
        /*self.*/insertDesignJsonObject(json.objects[index], index);
    }
})();

forEach runs synchronously, looping through all of your elements and scheduling a timer callback for each of them five seconds later. So five seconds later, all those callbacks happen.

Here in 2022, there are two primary approaches here:

  1. Use chained setTimeout with an index variable.

  2. Use an async function, await, and a loop.

Here's an example of #1:

const json = {
    objects: [
        {name: "first object"},
        {name: "second object"},
        {name: "third object"},
        {name: "fourth object"},
        {name: "fifth object"},
    ],
};

function insertDesignJsonObject(obj, index) {
    console.log(obj.name, index);
}

let index = 0; // The index of the next element to show
function doNext() {
    // Do this one, increment the index
    /*self.*/insertDesignJsonObject(json.objects[index], index);
    ++index;

    // If we still have one to do, do it after a delay
    if (index < json.objects.length) {
        setTimeout(doNext, 1000); // 5000 for a five-second delay
    }
}
doNext();

Here's #2:

const json = {
    objects: [
        {name: "first object"},
        {name: "second object"},
        {name: "third object"},
        {name: "fourth object"},
        {name: "fifth object"},
    ],
};

function insertDesignJsonObject(obj, index) {
    console.log(obj.name, index);
}

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

(async () => {
    for (let index = 0; index < json.objects.length; ++index) {
        // Wait to do this one until a delay after the last one
        if (index > 0) {
            await delay(1000); // 5000 for five seconds
        }

        // Do this one
        /*self.*/insertDesignJsonObject(json.objects[index], index);
    }
})();

猫九 2025-01-17 11:55:41
var i =0;
img_list.forEach(function(item){
    setTimeout(function(){
        img_class.setAttribute('src',item)
        console.log(item)
    },2000 * i++)
})
var i =0;
img_list.forEach(function(item){
    setTimeout(function(){
        img_class.setAttribute('src',item)
        console.log(item)
    },2000 * i++)
})
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文