叠层循环+超时有时会失败

Imbricated loops + timeout sometimes fails

我做了一个小脚本来练习,我的循环和它们的超时似乎有问题。 这是我脚本的 link:http://codepen.io/JulienBarreira/pen/EWNoxJ

有时写一个字,一两个字母写错。例如,我得到的不是 "cheeseburger",而是 "chkesebxrger"。

我发现了一个小技巧可以减少失败,但我完全不知道为什么。

  function charsAnim(i, word, j) {
    setTimeout(function() {
      var count = j; 
      if (j < steps) {           
        randomChar(i, word, count, j);
      } else {
        goodChar(i, word, count, j);
      }
      /* seems it fails less if I divide j, don't know why */
    }, (speed/steps)*(j / 1.8));
  }

当其他脚本 运行 在计算机上(例如在我的个人资料页面中)时,问题会更频繁地出现。

请随时给我关于我的代码的任何建议,即使这与我的问题无关。可能有更简单的方法来做同样的事情,我来这里是为了进步。

谢谢:)

编辑:我在片段中添加了 3 个 iframe 来向您展示问题,当您启动片段时,第一个词大部分时间都失败了。

var words = [
  'unicorn',
  'cheeseburger',
  'pizza',
  'pineapple',
  'popsicle',
  'bubbles',
  'seagull',
  'doodle',
  'goggles',
  'artichoke',
  'potato',
  'carrot',
  'vegeta'
];
var letters = "abcdefghijklmnopqrstuvwxyz#%&^+=-";
var speed = 250;
var steps = 4;

function getRandomWord() {
  var randomWord = words[Math.floor(Math.random() * words.length)];
  return randomWord;
}
function getRandomLetter() {
  var randomLetter = letters[Math.floor(Math.random() * letters.length)];
  return randomLetter;
}

function randomWordLoop() {
  var word = getRandomWord();
  var textLength = word.length;
  for(i = 0; i < textLength; i++) {    
    letterAppear(i, word);
  }  
  
  function letterAppear(i, word) {
    setTimeout(function() {
      randomLetters(i, word);    
    }, speed*i);  
  }

  function randomLetters(i, word) {
    for (j = 0; j <= steps; j++) {
      charsAnim(i, word, j);
    }
  }

  function charsAnim(i, word, j) {
    setTimeout(function() {
      var count = j; 
      if (j < steps) {           
        randomChar(i, word, count, j);
      } else {
        goodChar(i, word, count, j);
      }
      /* seems it fails less if I divide j, don't know why */
    }, (speed/steps)*(j / 1.8));
  }

  function randomChar(i, word, count, j) {
    var letter = getRandomLetter();
    if (j > 0) {
      var oldText = $('#loader').text().slice(0, -1);
    } else {
      var oldText = $('#loader').text();
    }
    $('#loader').text(oldText + letter);    
  }
  function goodChar(i, word, count, j) {
    var oldText = $('#loader').text().slice(0, -1);  
    $('#loader').text(oldText + word[i]);
    if (i == textLength - 1 ) {
      removeWord();
    }
  }
  
  function removeWord() {
    setTimeout(function() {
      for (k = 0; k < textLength; k++) {
        removeLetters(k);
      }
    }, speed*2);
  }
  function removeLetters(k) {
    setTimeout(function() {
      removeLetter(k);
    }, 75*k);
  }
  function removeLetter(k) {
    var actualText = $('#loader').text().slice(0, -1);
    $('#loader').text(actualText);
    if (k == textLength - 1) {
      randomWordLoop();
    }
  }
}

randomWordLoop();
body {
  background-color: #010101;
}
.loader {
  width: 300px;
  color: #0c9c73;
  text-align: left;
  font-size: 50px;
  font-family: Roboto Mono;
  font-weight: 700;
  text-transform: uppercase;
}
.loader:after {
    content:'_';
  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="https://fonts.googleapis.com/css?family=Roboto+Mono:400,700" rel="stylesheet">
<div class="loader" id="loader"></div>
<iframe width="560" height="315" src="https://www.youtube.com/embed/GquEnoqZAK0?autoplay=1" frameborder="0" allowfullscreen></iframe>
<iframe width="560" height="315" src="https://www.youtube.com/embed/GquEnoqZAK0?autoplay=1" frameborder="0" allowfullscreen></iframe>
<iframe width="560" height="315" src="https://www.youtube.com/embed/GquEnoqZAK0?autoplay=1" frameborder="0" allowfullscreen></iframe>

您正在组合这个循环,在 randomWordLoop()

for(i = 0; i < textLength; i++) {    
  letterAppear(i, word);
}

... letterAppear() 里面的 setTimeout()。基本上,当 letterAppear()letterAppear 内执行时,i 变量不再具有与设置超时时相同的值。它具有全局值,您页面中可能使用 i 的任何其他函数可能已经将其设置为完全不同的值。

此外,请注意设置 for 的正确方法不是全局使用 i,而是将其设置为函数的局部变量:for(var i = 0; i < textLength; i++) {...}.

你无法正确地看到它,因为你的函数输出随机字母并且没有视觉线索让你知道它运行在 i 的错误值上,但我相信你的函数大部分都是错误的时间。

要解决此问题,您需要 letterAppear() 中的闭包,它将 iword 的正确值传递给 randomLetters() 中的 setTimeout() , 当 setTimeout() 的内容执行时不考虑它们的全局值:

for(var i = 0; i < textLength; i++) {    
  (function(i,word){
    letterAppear(i, word);
  })(i,word)
}  

仔细观察您的代码,您可能需要在多个地方使用闭包(如果传递给函数的值在代码执行时相同很重要)并且您还应该定义 for本地迭代器(ij),使用 var,就像我在上面所做的那样。

不要忘记你最好的 JavaScript 朋友:

console.log(this, arguments);