loop with set interval inside

2019-09-05 16:48发布

I need to write a text one by one letter, with pausing 3 sec. between.

I tried this but doesn't work. After 3 sec. entire text is written at once.

function thanks() {   
    $("#writer").text("");
    var txt = "Thanks for visiting !";

    for ( var i = 0; i < 22; i++) {
      setInterval(function(){
        var type = txt.substring(0, i);
        $("#writer").text(type);
      }, 3000);
    };
};

2条回答
干净又极端
2楼-- · 2019-09-05 17:17

You're queuing up 22 calls at once, all happening 3 seconds later. Just create your interval once, outputting new text on each call. Stop the interval when you've run out of text.

function thanks() {   
  $("#writer").text("");
  var txt = "Thanks for visiting !";
  var i = 0;

  var int = setInterval( function() 
  {
    ++i;
    if (i > txt.length)
    {
      clearInterval(int);
      return;
    }

    var type = txt.substring(0, i);
    $('#writer').text(type);
  }, 3000);
}

Example: http://codepen.io/paulroub/pen/AwLfx

查看更多
你好瞎i
3楼-- · 2019-09-05 17:27

This is the classic closure problem, each function you're passing into setInterval has an enduring reference to i, not a copy of it, and so sees the same value of i (the value it has after the loop) when it runs. You also don't want to use setInterval here, you want setTimeout.

The usual answer is to have the function close over something else. These days that's really easy, you use Function#bind, which was added in ES5 but can be correctly shimmed for older JS engines.

But in addition to that, we also have something else to fix: We need to give each function a different delay, we can't use 3000 for all of them, or they'll all fire one right after another after three seconds! So rather than using a delay of 3000, we probably want to multiply that by i so each character is scheduled for 3000ms after the previous one.

I'd also use i <= txt.length rather than hardcoding the number. That way, if you change the text, there's no danger of forgetting to change the number, too.

There's also no need for (but no harm in) the ; after a function declaration. You need them after expressions, not declarations.

So all together:

function thanks() {
    $("#writer").text("");
    var txt = "Thanks for visiting !";
    for (var i = 0; i <= txt.length; i++) {
        setTimeout(function(index) {
            var type = txt.substring(0, index);
            $("#writer").text(type);
        }.bind(null, i), i * 3000);
    };
}

Function#bind returns a new function that, when called, will call the original function with a given this value (we don't need that bit, hence the null as the first argument) and then whatever arguments we give it (we're passing it i, which the function receives as the index argument).

Live Example (I used 300 rather than 3000 in the example, 3000 is really widely-spaced)

查看更多
登录 后发表回答