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

Бесконечная петля JavaScript?

Как создать бесконечный цикл в JavaScript? Я пытаюсь сделать слайд-шоу, которое у меня работает, но я не могу заставить его зацикливаться. Я даже не могу его зацикливать дважды.

Код, который я использую прямо сейчас, это

window.onload = function start() {
    slide();
}
function slide() {
    var num = 0;
    for (num=0;num<=10;num++) {
        setTimeout("document.getElementById('container').style.marginLeft='-600px'",3000);
        setTimeout("document.getElementById('container').style.marginLeft='-1200px'",6000);
        setTimeout("document.getElementById('container').style.marginLeft='-1800px'",9000);
        setTimeout("document.getElementById('container').style.marginLeft='0px'",12000);
    }
}

Без этого, он проходит один раз. Когда я вставляю, он либо заставляет Firefox блокироваться, либо просто циклы один раз. Я уверен, что это действительно простая вещь, и даже если она должна быть петлей 1 000 000 раз или что-то вместо бесконечного, это будет хорошо работать для меня.

Кроме того, я не хочу использовать jQuery или что-то, что кто-то создал. Я изучаю JavaScript, и это частично помогает мне учиться, и частично потому, что я пытаюсь сделать так много систем на базе HTML5, насколько смогу.

EDIT: Я думаю, что причина его замораживания заключается в том, что он выполняет код сразу, а затем просто хранит его в кеше или что-то в этом роде. То, что я хочу сделать, это пройти через это один раз, а затем снова начать сверху, и это то, что я всегда думал о том, где. В сценарии "пакетная" (командная строка) это можно сделать с помощью команды " GOTO ". Я не знаю, есть ли в JS эквивалент или нет, но это действительно моя цель.

4b9b3361

Ответ 1

Правильный подход - использовать один таймер. Используя setInterval, вы можете добиться того, чего хотите:

window.onload = function start() {
    slide();
}
function slide() {
    var num = 0, style = document.getElementById('container').style;
    window.setInterval(function () {
        // increase by num 1, reset to 0 at 4
        num = (num + 1) % 4;

        // -600 * 1 = -600, -600 * 2 = -1200, etc 
        style.marginLeft = (-600 * num) + "px"; 
    }, 3000); // repeat forever, polling every 3 seconds
}

Ответ 2

Вы не хотите while(true), который заблокирует вашу систему.

Вместо этого вам нужен тайм-аут, который устанавливает тайм-аут на себя, примерно так:

function start() {
  // your code here
  setTimeout(start, 3000);
}

// boot up the first call
start();

Ответ 3

Здесь хорошее, аккуратное решение для вас: (также посмотреть демо-версию → )

window.onload = function start() {
    slide();
}

function slide() {
    var currMarg = 0,
        contStyle = document.getElementById('container').style;
    setInterval(function() {
        currMarg = currMarg == 1800 ? 0 : currMarg + 600;
        contStyle.marginLeft = '-' + currMarg + 'px';
    }, 3000);
}

Поскольку вы пытаетесь научиться, позвольте мне объяснить, как это работает.

Сначала мы объявляем две переменные: currMarg и contStyle. currMarg - это целое число, которое мы будем использовать для отслеживания/обновления того, какой левый край должен иметь контейнер. Мы объявляем его вне фактической функции обновления (в closure), чтобы он мог постоянно обновляться/получать доступ, не теряя при этом значения. contStyle - это просто удобная переменная, которая дает нам доступ к стилям контейнеров без необходимости находить элемент на каждом интервале.

Затем мы будем использовать setInterval, чтобы установить функцию, которая должна вызываться каждые 3 секунды, пока мы не остановим ее (там ваш бесконечный цикл, не замораживая браузер). Он работает точно так же, как setTimeout, за исключением того, что он происходит бесконечно до отмены, а не только один раз.

Мы передаем анонимную функцию на setInterval, которая будет работать для нас. Первая строка:

currMarg = currMarg == 1800 ? 0 : currMarg + 600;

Это тернарный оператор. Он присваивает currMarg значение 0, если currMarg равно 1800, в противном случае оно увеличит currMarg на 600.

Во второй строке мы просто присваиваем наше выбранное значение container marginLeft, и мы закончили!

Примечание. Для демонстрации я изменил отрицательные значения на положительные, поэтому эффект будет виден.

Ответ 4

Perhps это то, что вы ищете.

var pos = 0;
window.onload = function start() {
    setTimeout(slide, 3000);
}
function slide() {
   pos -= 600;
   if (pos === -2400)
     pos = 0;
   document.getElementById('container').style.marginLeft= pos + "px";
   setTimeout(slide, 3000);
}

Ответ 5

Вы вызываете setTimeout() десять раз подряд, поэтому все они заканчиваются почти одновременно. То, что вы действительно хотите, это следующее:

window.onload = function start() {
    slide(10);
}
function slide(repeats) {
    if (repeats > 0) {
        document.getElementById('container').style.marginLeft='-600px';
        document.getElementById('container').style.marginLeft='-1200px';
        document.getElementById('container').style.marginLeft='-1800px';
        document.getElementById('container').style.marginLeft='0px';
        window.setTimeout(
          function(){
            slide(repeats - 1)
          },
          3000
        );
    }
}

Это вызовет слайд (10), который затем установит 3-секундный таймаут для вызова слайда (9), который установит таймаут для вызова слайда (8) и т.д. Когда вызывается слайд (0), больше нет тайм-ауты будут установлены.

Ответ 6

С помощью рекурсии вы можете бесконечно легко петлить.

function it_keeps_going_and_going_and_going() {
  it_keeps_going_and_going_and_going();
}

it_keeps_going_and_going_and_going()

Ответ 7

Ключ не в том, чтобы планировать все фотографии одновременно, а планировать следующий снимок каждый раз, когда вы показываете снимок.

var current = 0;
var num_slides = 10;
function slide() {
    // here display the current slide, then:

    current = (current + 1) % num_slides;
    setTimeout(slide, 3000);
}

Альтернативой является использование setInterval, которое заставляет функцию регулярно повторять (в отличие от setTimeout, которая планирует только следующий вид.

Ответ 8

В продолжение ответа Ender, давайте рассмотрим наши варианты с улучшениями от ES2015.


Во-первых, проблема в коде asker заключается в том, что setTimeout является асинхронным, а циклы - синхронными. Таким образом, логический недостаток заключается в том, что они записали несколько вызовов асинхронной функции из синхронного цикла, ожидая, что они будут выполняться синхронно.

function slide() {
    var num = 0;
    for (num=0;num<=10;num++) {
        setTimeout("document.getElementById('container').style.marginLeft='-600px'",3000);
        setTimeout("document.getElementById('container').style.marginLeft='-1200px'",6000);
        setTimeout("document.getElementById('container').style.marginLeft='-1800px'",9000);
        setTimeout("document.getElementById('container').style.marginLeft='0px'",12000);
    }
}

В действительности же происходит то, что...

  • Цикл "одновременно" создает 44 асинхронных тайм-аута, настроенных на выполнение 3, 6, 9 и 12 секунд в будущем. Аскер ожидал, что 44 вызова будут выполняться один за другим, но вместо этого все они будут выполняться одновременно.
  • Через 3 секунды после завершения цикла container marginLeft устанавливается в "-600px" 11 раз.
  • Через 3 секунды после этого для marginLeft устанавливается значение "-1200px" 11 раз.
  • 3 секунды спустя, "-1800px", 11 раз.

И так далее.

Вы можете решить это, изменив его на:

function setMargin(margin){
    return function(){
        document.querySelector("#container").style.marginLeft = margin;
    };
}

function slide() {
    for (let num = 0; num <= 10; ++num) {
        setTimeout(setMargin("-600px"), + (3000 * (num + 1)));
        setTimeout(setMargin("-1200px"), + (6000 * (num + 1)));
        setTimeout(setMargin("-1800px"), + (9000 * (num + 1)));
        setTimeout(setMargin("0px"), + (12000 * (num + 1)));
    }
}

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

Уроки, извлеченные из десятилетнего опыта

Как упоминалось в верхней части этого ответа, Эндер уже предложил решение, но я хотел бы добавить к нему, чтобы учесть передовой опыт и современные инновации в спецификации ECMAScript.

function format(str, ...args){
    return str.split(/(%)/).map(part => (part == "%") ? (args.shift()) : (part)).join("");
}

function slideLoop(margin, selector){
    const multiplier = -600;
    let contStyle = document.querySelector(selector).style;

    return function(){
        margin = ++margin % 4;
        contStyle.marginLeft = format("%px", margin * multiplier);
    }
}

function slide() {    
    return setInterval(slideLoop(0, "#container"), 3000);
}

Давайте рассмотрим, как это работает для начинающих (обратите внимание, что не все это напрямую связано с вопросом):

формат

function format

Чрезвычайно полезно иметь функцию форматирования строк, похожую на printf, на любом языке. Я не понимаю, почему в JavaScript его нет.

format(str, ...args)

... это шикарная функция, добавленная в ES6, которая позволяет вам делать множество вещей. Я считаю, что это называется оператором спреда. Синтаксис: ...identifier или ...array. В заголовке функции вы можете использовать его для указания аргументов переменных, и он будет принимать каждый аргумент в позиции и после позиции указанного аргумента переменной и помещать их в массив. Вы также можете вызвать функцию с массивом, например, так: args = [1, 2, 3]; i_take_3_args(...args) args = [1, 2, 3]; i_take_3_args(...args), или вы можете взять похожий на массив объект и преобразовать его в массив: ...document.querySelectorAll("div.someclass").forEach(...). Это было бы невозможно без оператора распространения, потому что querySelectorAll возвращает "список элементов", который не является истинным массивом.

str.split(/(%)/)

Я не очень хорошо объясняю, как работает регулярное выражение. JavaScript имеет два синтаксиса для регулярных выражений. Там путь OO (new RegExp("regex", "gi")) и там буквальный путь (/insert regex here/gi). Я испытываю глубокую ненависть к регулярным выражениям, потому что лаконичный синтаксис, который он поощряет, часто приносит больше вреда, чем пользы (а также потому, что они чрезвычайно непереносимы), но есть некоторые случаи, когда регулярное выражение полезно, как этот. Обычно, если вы вызываете split с помощью "%" или /%/, результирующий массив исключает разделители "%" из массива. Но для алгоритма, используемого здесь, мы должны включить их. /(%)/ было первое, что я попробовал, и это сработало. Я думаю, мне повезло.

.map(...)

map является функциональной идиомой. Вы используете карту, чтобы применить функцию к списку. Синтаксис: array.map(function). Функция: должна возвращать значение и принимать 1- 2 аргумента. Первый аргумент будет использоваться для хранения каждого значения в массиве, а второй будет использоваться для хранения текущего индекса в массиве. Пример: [1,2,3,4,5].map(x => x * x);//returns [1,4,9,16,25] [1,2,3,4,5].map(x => x * x);//returns [1,4,9,16,25]. Смотрите также: фильтр, поиск, уменьшение, forEach.

part => ...

Это альтернативная форма функции. Синтаксис: argument-list => return-value, например (x, y) => (y * width + x), что эквивалентно function(x, y){return (y * width + x);}.

(part == "%") ? (args.shift()) : (part)

Пара операторов ?: - это операндный оператор 3-, называемый троичным условным оператором. Синтаксис: condition? if-true: if-false condition? if-true: if-false, хотя большинство людей называют его "троичным" оператором, поскольку в каждом языке, в котором он присутствует, это единственный оператор-операнд 3-, каждый другой оператор является двоичным (+, &&, |, =) или унарный (++,..., &, *). Интересный факт: некоторые языки (и расширения языков поставщиков, такие как GNU C) реализуют двухоперационную версию оператора ?: С синтаксическим value?: fallback, который эквивалентен value? value: fallback value? value: fallback, и будет использовать fallback если value оценивается как ложное. Они называют это оператором Элвиса.

Я должен также упомянуть разницу между expression и expression-statement, поскольку я понимаю, что это может быть не интуитивно понятно для всех программистов. expression представляет значение и может быть присвоено l-value. Выражение может быть заключено в круглые скобки и не может считаться синтаксической ошибкой. Выражение само может быть l-value, хотя большинство операторов являются r-values, поскольку единственными выражениями l-значения являются выражения, сформированные из идентификатора или (например, в C) из ссылки/указателя. Функции могут возвращать l-значения, но не рассчитывают на это. Выражения также могут быть составлены из других, меньших выражений. (1, 2, 3) - это выражение, сформированное из трех выражений r-значения, соединенных двумя запятыми операторами. Значение выражения - 3. expression-statements, с другой стороны, являются операторами, сформированными из одного выражения. ++somevar является выражением, поскольку его можно использовать как значение r в выражении-выражении присваивания newvar = ++somevar; (значение выражения newvar = ++somevar, например, является значением, которое присваивается newvar). ++somevar; также выражение-утверждение.

Если троичные операторы вообще смущают вас, примените то, что я только что сказал, к троичному оператору: expression? expression: expression expression? expression: expression. Тернарный оператор может образовывать выражение или выражение-оператор, поэтому обе эти вещи:

smallest = (a < b) ? (a) : (b);
(valueA < valueB) ? (backup_database()) : (nuke_atlantic_ocean());

допустимы использования оператора. Пожалуйста, не делайте последнее, хотя. Это что, if это для. Есть случаи для такого рода вещей, например, в макросах препроцессора C, но мы говорим о JavaScript здесь.

args.shift()

Array.prototype.shift. Это зеркальная версия pop, якобы унаследованная от языков оболочки, где вы можете вызвать shift для перехода к следующему аргументу. shift "выталкивает" первый аргумент из массива и возвращает его, изменяя массив в процессе. Обратное не unshift. Полный список:

array.shift()
    [1,2,3] -> [2,3], returns 1
array.unshift(new-element)
    [element, ...] -> [new-element, element, ...]
array.pop()
    [1,2,3] -> [1,2], returns 3
array.push(new-element)
    [..., element] -> [..., element, new-element]

Смотрите также: ломтик, сплайс

.join("")

Array.prototype.join(string). Эта функция превращает массив в строку. Пример: [1,2,3].join(", ") → "1, 2, 3"

горка

return setInterval(slideLoop(0, "#container"), 3000);

Во-первых, мы возвращаем возвращаемое значение setInterval чтобы его можно было использовать позже при вызове clearInterval. Это важно, потому что JavaScript не будет очищать это сам по себе. Я настоятельно не рекомендую использовать setTimeout для создания цикла. Это не то, для чего предназначен setTimeout, и, делая это, вы возвращаетесь к GOTO. Прочитайте статью Дейкстры 1968 года "Перейти к утверждению, которое считается вредным", чтобы понять, почему циклы GOTO - плохая практика.

Во-вторых, вы заметите, что я сделал некоторые вещи по-другому. Повторяющийся интервал очевиден. Это будет продолжаться вечно, пока интервал не будет очищен, и с задержкой 3000 мс. Значением для обратного вызова является возвращаемое значение другой функции, которую я "#container" аргументам 0 и "#container". Это создает закрытие, и вы поймете, как это работает в ближайшее время.

slideLoop

function slideLoop(margin, selector)

Мы берем margin (0) и селектор ("#container") в качестве аргументов. Поля - это начальное значение поля, а селектор - это селектор CSS, используемый для поиска изменяемого элемента. Довольно просто.

const multiplier = -600;
let contStyle = document.querySelector(selector).style;

Я переместил некоторые из жестко закодированных элементов вверх. Поскольку поля кратны -600, у нас есть четко помеченный постоянный множитель с этим базовым значением.

Я также создал ссылку на свойство стиля элемента с помощью селектора CSS. Поскольку style - это объект, это безопасно сделать, так как он будет рассматриваться как ссылка, а не как копия (прочитайте статью Pass By Sharing, чтобы понять эту семантику).

return function(){
    margin = ++margin % 4;
    contStyle.marginLeft = format("%px", margin * multiplier);
}

Теперь, когда у нас есть определенная область, мы возвращаем функцию, которая использует указанную область. Это называется закрытием. Вы должны прочитать об этом тоже. Понимание, по общему признанию, странных правил определения содержания JavaScript сделает язык намного менее болезненным в долгосрочной перспективе.

margin = ++margin % 4;
contStyle.marginLeft = format("%px", margin * multiplier);

Здесь мы просто увеличиваем поле и модулируем его на 4. Последовательность значений, которые это произведет, будет 1->2->3->0->1->..., которая точно имитирует поведение вопрос без какой-либо сложной или жестко закодированной логики.

После этого мы используем определенную ранее функцию format чтобы безболезненно установить CSS-свойство marginLeft контейнера. Он установил текущее значение маржи, умноженное на множитель, который, как вы помните, был установлен на -600. -600 → -1200 → -1800 → 0 → -600 ->...


Между моей версией и версией Эндера есть некоторые важные различия, о которых я упоминал в комментарии к их ответу. Я сейчас перейду к рассуждениям:

Используйте document.querySelector(css_selector) вместо document.getElementById(id)

querySelector был добавлен в ES6, если я не ошибаюсь. querySelector (возвращает первый найденный элемент) и querySelectorAll (возвращает список всех найденных элементов) являются частью цепочки прототипов всех элементов DOM (не только document) и используют селектор CSS, поэтому есть другие способы найти элемент, кроме просто по его идентификатору. Вы можете выполнять поиск по идентификатору (#idname), классу (.classname), отношениям (div.container div div span, p:nth-child(even)) и атрибутам (div[name], a[href=https://google.com]), между прочим.

Всегда отслеживайте возвращаемое значение setInterval(fn, interval) чтобы впоследствии его можно было закрыть с помощью clearInterval(interval_id)

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

Поместите интервальный обратный вызов в его собственную формальную функцию для удобочитаемости и удобства обслуживания.

Конструкции как это

setInterval(function(){
    ...
}, 1000);

Может довольно легко стать неуклюжим, особенно если вы храните возвращаемое значение setInterval. Я настоятельно рекомендую поместить функцию вне вызова и дать ей имя, чтобы оно было четким и самодокументированным. Это также позволяет вызывать функцию, которая возвращает анонимную функцию, в случае, если вы делаете что-то с замыканиями (особый тип объекта, который содержит локальное состояние, окружающее функцию).

Array.prototype.forEach в порядке.

Если состояние сохраняется с обратным вызовом, обратный вызов должен быть возвращен из другой функции (например, slideLoop), чтобы сформировать замыкание

Вы не хотите смешивать состояние и обратные вызовы вместе, как это сделал Эндер. Это склонно к беспорядку и может быть трудно поддерживать. Состояние должно быть в той же функции, что и анонимная функция, чтобы четко отделить его от остального мира. Лучшим названием для slideLoop может быть makeSlideLoop, чтобы сделать его более понятным.

Используйте правильные пробелы. Логические блоки, которые делают разные вещи, должны быть разделены одной пустой строкой

Это:

print(some_string);

if(foo && bar)
    baz();

while((some_number = some_fn()) !== SOME_SENTINEL && ++counter < limit)
    ;

quux();

намного легче читать, чем это:

print(some_string);
if(foo&&bar)baz();
while((some_number=some_fn())!==SOME_SENTINEL&&++counter<limit);
quux();

Многие начинающие делают это. Включая маленького 14-летнего меня из 2009 года, и я не отучился от этой дурной привычки до, вероятно, 2013 года. Перестаньте пытаться сокрушить ваш код настолько маленьким.

Избегайте "string" + value + "string" +... Создайте функцию форматирования или используйте String.prototype.replace(string/regex, new_string)

Опять же, это вопрос читабельности. Это:

format("Hello %! You've visited % times today. Your score is %/% (%%).",
    name, visits, score, maxScore, score/maxScore * 100, "%"
);

намного легче читать, чем это ужасное чудовище

"Hello " + name + "! You've visited " + visits + "% times today. " + 
"Your score is " + score + "/" + maxScore + " (" + (score/maxScore * 100) +
"%).",

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

visits + "% times today"
          ^ whoops

Это хорошая демонстрация, потому что единственная причина, по которой я сделал эту ошибку и не замечал ее так долго (как не делал), в том, что код чертовски труден для чтения.

Всегда окружайте аргументы своих троичных выражений паренами. Это способствует удобочитаемости и предотвращает ошибки.

Я позаимствовал это правило из лучших практик, связанных с макросами препроцессора С. Но мне не нужно объяснять это; посмотреть на себя:

let myValue = someValue < maxValue ? someValue * 2 : 0;
let myValue = (someValue < maxValue) ? (someValue * 2) : (0);

Мне все равно, насколько хорошо вы думаете, что понимаете синтаксис вашего языка, последний будет ВСЕГДА легче читать, чем первый, и удобочитаемость - единственный необходимый аргумент. Вы читаете в тысячи раз больше кода, чем пишете. Не будьте придурком к своему будущему я в долгосрочной перспективе, просто чтобы похлопать себя по спине за то, что вы умны в краткосрочной перспективе.

Ответ 9

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

window.onload = function start() {
    slide();
}
function slide() {
     setInterval("document.getElementById('container').style.marginLeft='-600px'",3000);
     setInterval("document.getElementById('container').style.marginLeft='-1200px'",6000);
     setInterval("document.getElementById('container').style.marginLeft='-1800px'",9000);
     setInterval("document.getElementById('container').style.marginLeft='0px'",12000);
}

setInterval - это в основном "бесконечный цикл", и он не будет черным браузером. он ждет требуемое время, затем снова идет

Ответ 10

Вы можете использовать функцию requestAnimationFrame(), как показано ниже,

function unlimited () {
    requestAnimationFrame(unlimited);
    console.log("arian")
}

unlimited();