如何减缓JavaScript循环速度

12
我希望在以下循环的每次迭代中添加1-2秒的延迟。
<html>
<script type="text/javascript" src="http://maps.googleapis.com/maps/api/js?sensor=false"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>

<input id="start" type="submit"> </input>
<div id='status'></div>

<script>
var geocoder=new google.maps.Geocoder();                   
var glGeocodeCount = 0 ;

$(document).ready(function() {

    $('#start').click(function() {

        //srPerformGeocode("TD Tower, 55 King Street West, Toronto, ON, Canada, M5K 1A2");      

        for(x=0;x<20;x++){
            srPerformGeocode("TD Tower, 55 King Street West, Toronto, ON, Canada, M5K 1A2");
        }
        return false;
    });          
}); 

function srPerformGeocode(address){     
    if (geocoder){                
        geocoder.geocode({ 'address': address }, function (results, status) {                                                                              
            if (status == google.maps.GeocoderStatus.OK){                                                                                                                                                                           
                $('#status').prepend("Success : " + address + "<br/>");

            }
            else{
                $('#status').prepend("Failed : " + address + "<br/>");

            }
        });
    }
}
</script>

你为什么要对同一个地址进行20次地理编码? - ceejayoz
作为一个例子,我注意到一些API供应商(比如谷歌)不喜欢你频繁地敲他们的门。我想通过使用计时器来减少调用的频率,但在循环中使用它时遇到了问题。 - Mustapha George
没错,但是你为什么每次都要用同样的请求敲他们的门呢? - ceejayoz
2
我不会这样做,但是包含20(或200个)地址并不会对问题有太大的帮助。我的问题更多关于延迟循环。 - Mustapha George
6个回答

14

你可以使用 setTimeout() 来实现:

$(document).ready(function() {
    $('#start').click(function() {
        //srPerformGeocode("TD Tower, 55 King Street West, Toronto, ON, Canada, M5K 1A2");      
        var x = 0;

        function go() {
            srPerformGeocode("TD Tower, 55 King Street West, Toronto, ON, Canada, M5K 1A2");
            if (x++ < 20) {
                setTimeout(go, 2000);
            }
        }
        go();

        return false;
    });          
}); 

这确实让我想知道为什么你要连续20次对完全相同的地址进行地理编码查询?


7

现代 JS 解决方案:

function timeout(ms) {
    return new Promise(resolve => setTimeout(resolve, ms))
}

async function slowedCode() {
    console.log("Before Delay")
    await this.timeout(Math.random() * 2000 + 500) // Wait random amount of time between [0.5, 2.5] seconds
    console.log("After Delay")
}

async function slowedForLoop() {
    const data = ["1","2","3","4","5"]
    for (let d of data) {
        console.log(d)
        await this.timeout(Math.random() * 100 + 500)
    }    
}

唯一的缺点是必须从异步函数内部执行延迟。

6

在这种情况下,您可能需要使用计时器。如果您只是在代码中放置一个延迟循环,结果只会导致代码运行时间更长,但是最终结果将在代码完成后一次性显示出来。

您可以使用setTimeout或setInterval方法。示例:

function(){

var instructions = [
function() { /* do something */ },
function() { /* do something */ },
function() { /* do something */ },
function() { /* do something */ }
];

var index = 0;

var handle = window.setInterval(function() {
if (index < instructions.length) {
  instructions[index++]();
} else {
  window.clearInterval(handle);
}
}, 10);

}();

1

我有一种感觉,你宁愿等到地理编码查找真正完成后再开始下一个循环迭代。所以,关键词是“回调”:

不要使用for...,请按照以下方式操作。我知道这可能不是你习惯的东西,但请尝试理解它(它应该可以工作)。

var dogeo = function(callback)
{
    srPerformGeocode("address", callback);
};

var counter = 0;

var geoCallback = function()
{
         counter++;

         if(counter < 20)
         {
             dogeo(geoCallback);
         }

};


dogeo(geoCallback);



function srPerformGeocode(address, callback){     
    if (geocoder){                
        geocoder.geocode({ 'address': address }, function (results, status) {    


           // this function is a callback of geocode()

            if (status == google.maps.GeocoderStatus.OK){                                                                                                                                                                           
                $('#status').prepend("Success : " + address + "<br/>");

            }
            else{
                $('#status').prepend("Failed : " + address + "<br/>");

            }

            callback(); // let the caller know this is done
        });
    }
}

1
使用setTimeout/setInterval暂停可能对您有效,但这是一种幻觉。如果我能正确地阅读您的代码,原始问题在于您的循环比geocoder.geocode每次返回的速度更快。因此,在启动下一个之前,不进行并行地地理编码调用的唯一方法是等待一个完成。也就是说,如果您只是想避免并行调用,就没有必要进一步减慢循环。如果您仍然想在调用之间暂停,应该仍然使用回调与setTimeout结合使用。 - user191966
是的,我想避免并行调用以及减慢循环速度。似乎没有办法使谷歌API同步执行,所以我必须将所有内容堆叠到它们的成功函数中。我会尝试理解你的代码... - Mustapha George
以上代码消除了并行调用;如果需要在调用之间暂停,请使用以下代码:if(counter < 20) { window.setTimeout(function(){ dogeo(geoCallback); } }, 2000); - user191966

1
我最终使用模运算符来实现这个目的。然后,您可以增加/减少它所除以的数量来增加/减少延迟时间。不过,没有for循环可能会更好。
$(document).ready(function() {

    $('#start').click(function() {
      
        var delay = 0;
        for(x=0;x<20;x){
            delay++;
            if (delay % 10 == 0) {
                srPerformGeocode("TD Tower, 55 King Street West, Toronto, ON, Canada, M5K 1A2");
                x++;
            }
        }
        return false;
    });          
}); 

0

我鼓励摆脱循环,使用setTimeout:

    $('#start').click(function() {
        var i = 0, max = 20, delay = 2000, run;
        run = function(){
           srPerformGeocode("TD Tower, 55 King Street West, Toronto, ON, Canada, M5K 1A2");
           if(i++ < max){
              setTimeout(run, delay);
           }
        }
        run();
        return false;
    });

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