Подтвердить что ты не робот

SetTimeout внутри цикла

Я хочу, чтобы строка появлялась для символа-символа со следующим кодом:

function initText()
{
    var textScroller = document.getElementById('textScroller');
    var text = 'Hello how are you?';

    for(c = 0; c < text.length; c++)
    {
        setTimeout('textScroller.innerHTML += text[c]', 1000);
    }
}

window.onload = initText;

Это не работает. Что я делаю неправильно?

4b9b3361

Ответ 1

Попробуйте что-то вроде этого:

function initText()
{
    var textScroller = document.getElementById('textScroller');
    var text = 'Hello how are you?';

    var c = 0;
    var interval = setInterval(function() { 
                          textScroller.innerHTML += text[c]; 
                          c++; 
                          if(c >= text.length) clearInterval(interval);
                   }, 1000);

}

Примечание. Я добавил clearInterval, чтобы остановить его, когда это необходимо.

Ответ 2

В настоящее время вы определяете 18 тайм-аутов, и все будут выполняться ~ сразу. Вторая проблема заключается в том, что вы передаете инструкции для выполнения в виде строки. В этом случае код не будет иметь доступа ко всем переменным, определенным в initText, потому что оцениваемый код будет выполнен в глобальной области.

IMO, это должно выполнить работу

function initText(){
    var textScroller = document.getElementById('textScroller');
    var text = 'Hello how are you?';

    var c = 0;

    (function(){
        textScroller.innerHTML += text.charAt(c++);
        if(text.length > c){
            setTimeout(arguments.callee, 1000);
        }
    })();
}

Ответ 3

Еще более общий, чем answer @yauhen-yakimovich:

Использование Timeout:

var repeat = (function () {
    return function repeat(cbWhileNotTrue, period) {
        /// <summary>Continuously repeats callback after a period has passed, until the callback triggers a stop by returning true.  Note each repetition only fires after the callback has completed.  Identifier returned is an object, prematurely stop like `timer = repeat(...); clearTimeout(timer.t);`</summary>

        var timer = {}, fn = function () {
            if (true === cbWhileNotTrue()) {
                return clearTimeout(timer.t); // no more repeat
            }
            timer.t = setTimeout(fn, period || 1000);
        };
        fn(); // engage
        return timer; // and expose stopper object
    };
})();

Используя Interval:

var loop = (function () {
    return function loop(cbWhileNotTrue, period) {
        /// <summary>Continuously performs a callback once every period, until the callback triggers a stop by returning true.  Note that regardless of how long the callback takes, it will be triggered once per period.</summary>

        var timer = setInterval(function () {
            if (true === cbWhileNotTrue()) clearInterval(timer);
        }, period || 1000);
        return timer; // expose stopper
    };
})();

Небольшая разница между двумя указанными в комментариях - метод repeat повторяется только после выполнения обратного вызова, поэтому, если у вас есть "медленный" обратный вызов, он не будет запускать каждый delay ms, но повторяется после каждого delay между выполнением, тогда как метод loop будет запускать обратный вызов каждые delay ms. Чтобы преждевременно остановиться, repeat использует объект как возвращаемый идентификатор, поэтому используйте clearTimeout(timer.t) вместо этого.

Использование:

Также как ответ на @soufiane-hassou:

var textScroller = document.getElementById('textScroller');
var text = 'Hello how are you?';

var c = 0;
var interval = repeat/* or loop */(function() { 
                      textScroller.innerHTML += text[c]; 
                      c++; 
                      return (c >= text.length);
               }, 1000);

Как уже упоминалось, преждевременная остановка была бы следующей:

/* if repeat */ clearTimeout(interval.t);
/* if loop */   clearInterval(interval);

Ответ 4

Попробуйте следующее:

function initText()
{
    var textScroller = document.getElementById('textScroller');
    var text = 'Hello how are you?';

for(c = 0; c < text.length; c++)
{
    setTimeout("textScroller.innerHTML += '" + text[c] + "'", 1000 + c*200);
}
}

window.onload = initText;

Ответ 5

Попробуйте использовать закрытие:

function init() {
    var textScroller = document.getElementById('textScroller');
    var text = 'Hello how are you?';
    var c = 0;
    function run() {
        textScroller.innerHTML += text[c++];
        if (c<text.length)
            setTimeout(run, 1000);
    }
    setTimeout(run, 1000);
}
init()

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

Ответ 6

Я хочу поделиться фрагментом (на основе ответа Soufiane Hassou). Это распространяется на случай, когда вы буквально заменяете тело for-loop, которое должно повторяться через некоторый массив за определенный промежуток времени. В основном такой же синхронный цикл, но с паузой "sleep" (поскольку javascript не является синхронным языком программирования).

function loop(arr, take, period) {
    period = period || 1000;
    var i = 0;
    var interval = setInterval(function() { 
        take(i, arr[i]);
        if (++i >= arr.length) { clearInterval(interval);}
    }, period);
}

Пример использования:

loop([1, 2, 3, 4], function(index, elem){
    console.log('arr[' + index + ']: ' + elem);
});

Протестировано в Node JS. Надежда, которая помогает кому-то.

изменить >

следующее обновление позволяет использовать код вместе с libs, делая тяжелые "прототипы" (например, jQuery или прототип):

function loop(arr, take, period) {
    period = period || 1000;
    var scope = {
        i: 0,
        arr: arr,
        take: take,
    };
    var iterate = (function iterate() {
        if (this.i >= this.arr.length) { clearInterval(this.interval); return}
        take(this.i, this.arr[this.i++]);
    }).bind(scope);
    scope.interval = setInterval(iterate, period);
}

Ответ 7

Ваш цикл for устанавливает тайм-аут для каждого символа одновременно, поэтому они не будут отображаться последовательно, но все сразу. Ваш setTimeout должен включать код в другой setTimeout, который будет включать следующий отображаемый символ.

Так что-то вроде этого (не проверял это)

function initText()
{
    var textScroller = document.getElementById('textScroller');
    var text = 'Hello how are you?';    
    setTimeout('nextChar(text)', 1000);
}
function nextChar(text){
    if(text.length > 0){
        textScroller.innerHTML += text[0]; 
        setTimeout('nextChar(text.substring(1))', 1000);
    }
}

Ответ 8

Если вы хотите сохранить setTimeOut (вместо setInterval) и использовать именованную функцию (вместо оценки кода в вызове setTimeOut), это может быть полезно:

var b = {
  textScroller: document.getElementById('textScroller'),
  text: "Hello how are you?"
};


function initText() {
  for(c = 0; c < b.text.length; c++) {
    setTimeout("append("+c+")", 1000 + c*200);
  }
}

function append(c) {
  b.textScroller.innerHTML += b.text[c];
}

window.onload = initText;

С приведенным выше вы можете передать параметр для добавления функции.

Чтобы передать несколько параметров, следующий код выполняет трюк:

var glo = [];

function initText()
{
  var textScroller = document.getElementById('textScroller');
  var text = "Hello how are you?";
  var timeout_time;
  for(c = 0; c < text.length; c++) {
    glo[glo.length] = {text:text, c:c, textScroller:textScroller};
    timeout_time = 1000 + c * 200;
    setTimeout("append(" + (glo.length - 1) + ")", timeout_time);
  }
}

function append(i)
{
  var obj = glo[i];
  obj.textScroller.innerHTML += obj.text[obj.c];
  obj = null;
  glo[i] = null;
}

window.onload = initText;

С вышесказанным вы имеете только один глобальный массив glo. В цикле вы создаете новых членов массива в glo и в функции append() ссылаетесь на этих членов, используя индекс, который передается как параметр.

ПРЕДОСТЕРЕЖЕНИЕ: второй образец кода не предназначен как наилучшее или наиболее подходящее решение проблемы OP: s, но может пригодиться в других относительных проблемах setTimeOut, например. когда кто-то хочет сделать презентацию или тест производительности, где некоторые функции необходимы для вызова после некоторой задержки. Преимущество этого кода состоит в том, чтобы использовать для циклов (многие кодеры хотят использовать для циклов) и возможность использовать также внутренние циклы и возможность "отправлять" локальные переменные в их состоянии времени цикла в timeOut.

Ответ 9

Может быть лучше петля в каскаде. Например, чтобы исчезнуть div:

div=document.createElement('div');
div.style.opacity=1;
setTimeout(function(){fade(1);},3000);
function fade(op){
    op-=.05;
    if(op>0) setTimeout(function(){div.style.opacity=op;fade(op);},30);
    else document.body.removeChild(div);
}