Javascript фрагмент машинки заставляет браузер не отвечать - PullRequest
2 голосов
/ 20 марта 2020

У меня был подход, чтобы добавить эффект пишущей машинки к моему тексту, используя Javascript. Для этого я использовал следующий фрагмент:

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)
}

Я знаю, что этот подход кажется нелепым, но мне любопытно, почему он это делает. Когда я пытаюсь запустить этот фрагмент, браузер (Chrome) перестает отвечать на запросы. Я хочу знать, генерирует ли этот код бесконечный l oop и почему? И если кто-то может предоставить мне какой-то альтернативный способ получить желаемый результат.

Ответы [ 3 ]

2 голосов
/ 20 марта 2020

Поскольку counter в setTimeout не совпадает с counter на внешней стороне. Если вы сделаете консольный вход в систему через некоторое время, он все равно будет 0.

Это связано с областями видимости, что здесь слишком много, чтобы объяснить, но это то, что вы ищете за. Простое объяснение состоит в том, что внутри function(){}, который теперь применяется в setTimeout(), переменные вне этой функции не существуют.

Поскольку это не одна и та же переменная, внешняя никогда не бывает обновляется, поэтому ваш l oop никогда не прекращает добавлять больше тайм-аутов, поэтому ваш браузер никогда не останавливается.


Кроме того, в вашем текущем коде все символы будут появляться одновременно, через 1000 мс , SetTimeout не заставляет время "ждать" 1000 мс, оно просто говорит javascript "сделать это, после 1000" и затем продолжается. Вы создаете что-то вроде очереди, приказывая всем им выполнить эту задачу через 1000 мс.

Решение обеих проблем (но не забывайте о масштабах!) Заключается в использовании этих знаний в ваших интересах:

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

Теперь мы все снова помещаем их в эту «очередь», но на этот раз мы просим каждого следующего ждать больше 1000 мс.

1 голос
/ 20 марта 2020

setTimeout - это не «сон в течение 1 с c, затем выполните код и продолжите».

Что setTimeout делает, добавляет код в «кучу» и продолжает работать до следующей строки. По истечении времени ожидания (1000 мс) выполняется выполнение кода из кучи.

Итак, ваша функция продолжает добавлять код в кучу в течение 1000 мс, пока не сработает первый тайм-аут (он не сработает, потому что вы продолжаете добавлять новые таймауты - процесс застрял), когда предполагается увеличить счетчик.

0 голосов
/ 20 марта 2020

Спасибо @ Martijn & @ Alex Brohshtut за помощь в распознавании проблемы и предложение полезных решений.

Я поддерживал свою код, используя их уведомления и, просматривая inte rnet, и, наконец, я сделал это следующим образом:

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. Я изменил значение переменной counter только внутри функции обратного вызова setTimeout, и ее значение не изменилось внутри l oop по причинам области действия. Так что это был бесконечный l oop, который заставил мой браузер не отвечать ни на одну ночь.

    решение : мне пришлось изменить значение counter вне функции обратного вызова

  2. Я неправильно понял функцию setTimeout, подумал, что сценарий остановится на некоторое время, а затем продолжится. Два друга показали мне, что он только планирует функцию обратного вызова внутри нее, чтобы она выполнялась через определенное время c. Поэтому мне приходилось увеличивать это время на каждой итерации.

    решение : я воспользовался советом @ Martijn и использовал 1000 * counter++ в качестве времени для него.

  3. Когда я выполняю функцию внутри setTimeout, выполняемую через 1000 мс, l oop продолжается и увеличивает значение counter. поэтому, когда функция выполняется, счетчик теперь имеет самое высокое значение, равное content.length.

    решение : я использовал вызов функции, которая возвращает саму функцию обратного вызова, но это время со счетчиком в качестве аргумента. Это показалось мне немного запутанным, но в конце концов все заработало.

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

Другой подход, который я видел в учебнике YouTube ЗДЕСЬ

Использование 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);
...