【问题标题】:Javascript typewriter snippet makes browser not respondingJavascript打字机片段使浏览器没有响应
【发布时间】:2020-07-01 15:34:38
【问题描述】:

我有一种方法可以使用Javascript 为我的文本添加打字机效果。我使用以下 sn-p 这样做:

const text = document.querySelector('.type')

const content = text.textContent
let counter = 0
text.textContent = ''
while (counter < content.length) {
    setTimeout(function () {
        text.textContent += content[counter];
        counter += 1
    }, 1000)
}

我知道这种方法看起来很荒谬,但我很好奇为什么会这样。 当我尝试运行这个 sn-p 时,浏览器 (Chrome) 没有响应。 我想知道这段代码是否会产生无限循环,为什么?如果有人可以为我提供一些替代方法来获得想要的结果。

【问题讨论】:

    标签: javascript html google-chrome while-loop infinite-loop


    【解决方案1】:

    因为 setTimeout 中的 counter 与外部的 counter 不同。如果您在一段时间后执行控制台登录计数器,它仍然是 0。

    这与作用域有关,这里解释的有点过多,但这就是您要寻找的。简单的解释是,在 function(){} 内部,现在在 setTimeout() 中应用,该函数外部的变量不存在。

    因为它不是同一个变量,所以外面的那个永远不会更新,所以你的循环永远不会停止添加更多的超时,所以你的浏览器永远不会停止。


    除此之外,在您当前的代码中,所有字符将在 1000 毫秒后同时出现。 setTimeout 不会让 while“等待”1000 毫秒,它只是告诉 javascript“在 1000 之后执行此操作”然后继续。你创建一个类似队列的东西,告诉他们所有人在 1000 毫秒后完成该任务。

    这两个问题的解决方案(但一定要阅读范围!),就是利用这些知识为您带来优势:

    while (counter < content.length) {
        setTimeout(function () {
            text.textContent += content[counter];
        }, 1000 * counter++)
    }
    

    现在我们再次将它们放入那个“队列”,但这一次我们告诉下一个要再等 1000 毫秒。

    【讨论】:

    • 我会尝试修改它
    • 现在它每秒打印一次undefined,而不是打印字符
    • 没错。因为同样的原则也适用于content :) 它只是你的function(){} 中的一个未定义变量。要更具体地了解发生了什么,您需要阅读“javascript 中的作用域”。有很多教程可以比我更好地解释它
    • 我认为content 是在包含setTimeout 函数的循环内定义的,所以它的值可以被函数读取。问题是当我让setTimeout 中的行在 1000 毫秒后执行时,循环继续并增加计数器的值。所以当这些行被执行时,计数器现在有它的最大值,即content.length
    • 这就是我错误地将counter++ 添加到setTimeout 函数的原因。
    【解决方案2】:

    setTimeout 不是“睡眠 1 秒然后执行代码然后继续”。

    setTimeout 将其代码添加到“堆”并继续运行到下一行。超时时间(1000ms)后,堆中的代码被执行。

    因此,您的函数会在 1000 毫秒内不断向堆中添加代码,直到触发第一个超时(它不会触发,因为您一直在添加新的超时 - 进程被卡住),此时它应该增加计数器.

    【讨论】:

    • 您到底想达到什么目标?此代码将在执行前等待 1 秒:const text = document.querySelector('.type') const content = text.textContent let counter = 0 text.textContent = '' let timeoutId; const increment = function() { text.textContent += content[counter]; counter += 1 clearTimeout(timeoutId); }; const scheduleIncrement = function() { if (!timeoutId &amp;&amp; counter &lt; content.length) timeoutId = setTimeout(function() { increment(); }, 1000) }
    【解决方案3】:

    感谢 @Martijn@Alex Brohshtut 帮助我发现问题并提供有用的解决方案。

    我确实使用他们的通知并通过互联网查找来维护我的代码,最后我以这种方式做到了:

    const text = document.querySelector('.type')
    
    const content = text.textContent
    let counter = 0
    text.textContent = ''
    
    while (counter < content.length) {
        setTimeout((function (counterCopy) {
            return function () {
                text.textContent += content[counterCopy];
            }
        }(counter)), 1000 * counter++)
    }
    

    这是我的第一个代码的问题:

    1. 我在setTimeout的回调函数中修改了变量counter的值only,由于作用域的原因,它的值在循环内部没有改变。所以这是一个无限循环,导致我的浏览器一夜没有响应。

      解决方案:我不得不在回调函数之外更改counter 的值

    2. 我误解了setTimeout 函数,以为它会使脚本停止一段时间然后继续。两位朋友向我展示了它只安排其中的回调函数在特定时间后执行。所以,我不得不在每次迭代中增加这个时间。

      解决方案:我接受了@Martijn的建议,并使用1000 * counter++作为它的时间。

    3. 当我让setTimeout 中的函数在 1000 毫秒后执行时,循环继续并增加 counter 的值。所以当函数被执行时,计数器现在的最大值等于content.length

      解决方案:我使用了一个函数调用,它返回回调函数本身,但这次将计数器作为参数。这让我有点困惑,但最后效果很好。

      setTimeout((function(counterCopy){
          return function() {
              console.log(counterCopy)
          }
      }(counter)), 1000 * counter++)
      

    我在 YouTube 教程 HERE 中看到的另一种方法

    使用setInterval 代替setTimeout

    const text = document.querySelector('.type')
    
    const content = text.textContent
    let counter = 0
    text.textContent = ''
    
    var typewriter = setInterval(function(){
        text.textContent += content[counter];
        counter++;
        if (counter > (content.length - 1)) {
            clearInterval(typewriter);
        }
    }, 1000);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多