setTimeout和递归IFFE,嵌套!?如何计算延迟。

时间:2017-09-11 20:52:27

标签: javascript loops recursion

这个问题确实让我想起了我对JS技能的看法...... :(

我觉得我是正确的,但是我很难理解概念的做法,这不是一个语法问题,因为我觉得我几乎破解了它。

我需要什么

  • 我正在尝试console.log一系列字符串,一次一个字母。
  • 输出的每个字母之间需要有一个延迟,比如说300ms。
  • 每输出一个STRING之间必须有一个延迟,比如2000ms。
  • 示例数组中有2个字符串,但解决方案必须支持动态数量的字符串。

我的当前代码(可以粘贴到控制台中)

var stringList = ['first test','second test'],
    stringListLen = stringList.length;      

for(var i = 0; i < stringListLen; i++){

    // begin with first string and read it's length for iterations        
    var stringNumber = 0;
    var currentString = stringList[stringNumber];
    var currentStringLen = currentString.length;

    // just see the word for clarification at this point in code
    console.log(currentString);


    (function (i) {
        setTimeout(function () {                

            for (var j = 0; j < currentStringLen; j++) {

              (function (j) {
                setTimeout(function () {

                    // THE MAGIC HAPPENS HERE
                    console.log(j, currentString.charAt(j));

                    // End of string, so read next string, reset letter count
                    if(j === currentStringLen - 1){
                        stringNumber++;
                        currentString = stringList[stringNumber];
                        j = 0;                      
                    }

                }, 300 * j); // each letter * specified delay
              })(j);
            };

        }, currentStringLen * 300 * i); // letter * delay * letters in word
    })(i);      


}

问题

好的:我成功地获得了输出的字母之间的短暂延迟,并且当我们到达第一个单词的结尾时,我的检查切换到一个新单词并重置字母计数器...

坏了:我不能让两个词之间的等待工作。我已经尝试了一些想法,让自己感到困惑,我不知道我的方法现在是否正确。

THE UGLY:上一个词的最后一个字母也没有输出,这完全出乎意料。

我尝试了什么。

好吧,我尝试过简单地将“currentStringLen * 300 * i”元素更改为看似合乎逻辑但效果更好或更差的各种组合。最终我想我正在尝试计算“等待当前字符串中的字母数300(字母延迟)*”&lt; ---- STRIKETHROUGH ...

我实际上不知道我在计算什么,这就是问题。

我现在认为我想把它分成两个函数,而不是两个嵌套函数。一个字符串READ AND PASS到另一个JUST输出短暂延迟字母的函数,然后一旦到达最后一个字母,它就会调用第一个函数询问下一个字。但是,我仍然需要递归数组中产生同样问题的字符串数量......

我在这里错过了什么基本的人吗?

4 个答案:

答案 0 :(得分:1)

这大致是你想到的吗?

function printLetters(stringList) {
    var wordIndex = 0,
        letterIndex = 0;

    printNext();

    function printNext() {
        var word = stringList[wordIndex];
        var letter = word.charAt(letterIndex);

        console.log(letter);

        ++letterIndex;

        if (letterIndex === word.length) {
            letterIndex = 0;
            ++wordIndex;

            if (wordIndex < stringList.length) {
                setTimeout(printNext, 2000);
            }
            
            return;
        }

        setTimeout(printNext, 300);
    }
}

printLetters(['first test', 'second test']);

此处只有一个setTimeout同时运行,并且根据需要在适当的时间设置新的。{/ p>

虽然我不建议同时运行多个计时器,但可以完成。像这样:

function printLetters(stringList) {
    var letterCount = 0,
        startTime = Date.now();

    stringList.forEach(function(word, wordCount) {
        word.split('').forEach(function(letter) {
            setTimeout(function() {
                console.log(letter, Date.now() - startTime);
            }, wordCount * 1700 + (letterCount * 300));

            ++letterCount;
        });
    });
}

printLetters(['first test', 'second test']);

这里我已经在日志记录中包含了时间增量,以便更好地了解何时发生的事情。字符串之间的差距是2000,但代码中的常量是1700,因为已经添加了300个。

答案 1 :(得分:1)

我会做一个完全不同的方法。我不会做一堆预先计算的超时及其关联的闭包,而是一次只执行一次超时,使用递归然后继续下一次超时:

function delayShow(words) {
   if (!words || words.length === 0) {
      return;
   } else if (words[0].length === 0) {
      words.shift()
      setTimeout(() => delayShow(words), 2000);
   } else {
      console.log(words[0].charAt(0));
      words[0] = words[0].substr(1);
      setTimeout(() => delayShow(words), 300);
   }
}

delayShow(['first test','second test']);

答案 2 :(得分:0)

你可以在循环中使用一个条件,当你在字符串字符的最后一次迭代时,你会在超时内再次调用递归函数来迭代数组中的下一个字符串等。

&#13;
&#13;
var stringList = ['first test', 'second test'];


(function rec(j) {
  var chars = stringList[j].split('');

  chars.forEach(function(char, i) {
    setTimeout(function() {

      console.log(char);

      // if it's the last iteration && there are more strings in the array
      if ((i == (chars.length - 1)) && (j < stringList.length - 1)) {
        setTimeout( function() {
          rec(++j); // play it again
        }, 2000);
      }

    }, i * 300);
  });

})(0);
&#13;
&#13;
&#13;

答案 3 :(得分:0)

即使像setTimeout这样微不足道的东西,也是使用async / await的一个很好的例子。所以我在下面列举了一个例子。

正如您将看到的,代码更容易理解。具有不创建多个同时运行的setTimeout的附加优点。

它还有助于使事情变得更容易,例如。如果我要求你改变下面的代码,以便SPACE花费的时间少于其他字母,为了让它更自然地流动,它不会花很多心思去研究改变什么。

&#13;
&#13;
var 
  stringList = ['first test','second test'];
  
async function delay(ms) {
  return new Promise((resolve, reject) => {
    setTimeout(resolve, ms);
  });
}

async function run() {
  let wordpos = 0;
  let wordElem = document.querySelector('.words');
  while (true) {
    let word = stringList[wordpos]; 
    let text = '';
    for (var letterpos = 0; letterpos < word.length; letterpos ++ ) {
      let letter = word[letterpos];
      text = text + letter;
      wordElem.innerText = text;
      await delay(300);
    }
    await delay(2000);
    wordpos = (wordpos + 1) % stringList.length;
  }
}

run();
&#13;
<h1 class="words">?</h1>
&#13;
&#13;
&#13;