在forEach循环中使用setTimeout

3
我想在我的forEach循环中延迟调用另一个函数,如果满足某个条件,但是在这种情况下我不理解setTimeout。
function checkName(person) {
    console.log('checking name of ' + person.name)
    if (person.name === 'Julie') return true 
}

function checkPersons() {
    var persons = [
        {name: 'Bob', age: 21},
        {name: 'Frank', age: 15},
        {name: 'Julie', age: 12}
    ]

    var results = []

    persons.forEach(function(person) {
        if (person.age >= 18) {
            console.log('do not need to check name of ' + person.name)
            results.push(person)
        } else {
            setTimeout(function() {
                if (checkName(person)) {
                    console.log('Julie is ' + person.name)
                    results.push(person)
                }
            }, 5000)
        }        
    }) 
}

checkPersons()

https://jsfiddle.net/nicholasduffy/sy7qpqu1/1/

我得到

do not need to check name of Bob
// 5 second delay here
checking name of Frank
checking name of Julie
Julie is Julie

我想在每次调用 checkName 时增加5秒的延迟。
do not need to check name of Bob
// 5 second delay here
checking name of Frank
// 5 second delay here
checking name of Julie
Julie is Julie

JS异步触发超时函数,这意味着循环结束后,setTimeout的回调将在5秒后被调用。循环本身不会等待回调被调用。因此,两个回调几乎同时触发。@juvian的评论是一种解决方法,以获得所需的行为。 - martinczerwi
问题在于你的两个超时定时器同时被创建。setTimeout 会释放主线程以继续执行,因此这两个调用几乎是瞬间发生的。如果你按照 @juvian 的建议去做,那么超时定时器的时间将会是索引值的倍数。 - Liam
明白了,谢谢。这个快速修复现在已经起作用了。@juvian 如果你想回答,我会接受的。 - duffn
2个回答

7

正如其他人所提到的,setTimeout是异步的,因此js会在forEach中触发所有的超时函数,并等待5秒钟。因此,在5秒后,它们都会“同时”运行。

为了避免这种情况,您可以使用队列并仅运行一个timeout,当完成后调用下一个timeout,或者在这种情况下,一个更简单的解决方案是根据您正在迭代的人的索引调整等待时间:

persons.forEach(function(person, index) { // we add index param here, starts with 0
    //your code
    else{
        setTimeout(function() {
            if (checkName(person)) {
                console.log('Julie is ' + person.name)
                results.push(person)
            }
        }, 5000*(index+1)) // or just index, depends on your needs
    }        
}) 

这样,第一个将在5秒后运行,第二个在10秒后运行,第三个在15秒后运行,以此类推。


0
var index = 1;
persons.forEach(function(person) {
        if (person.age >= 18) {
            console.log('do not need to check name of ' + person.name)
            results.push(person)
        } else {
            setTimeout(function() {
                if (checkName(person)) {
                    console.log('Julie is ' + person.name)
                    results.push(person)
                }
            }, (index)*5000);
            index++;
        }        
    }) 

2
请不要只是简单地贴上代码而没有解释 - 即使仔细检查也不清楚您所做的更改。 - James Thorpe
很好的观点@JamesThorpe,用于存档参考,“index”仅在启动“setTimeout”时递增。 - Ross The Boss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接