Стоп для цикла, используя разрыв на клик - PullRequest
0 голосов
/ 30 августа 2018

У меня есть этот простой цикл из 1-20. То, что я пытаюсь сделать, это остановить цикл с помощью нажатия кнопки. Я установил условие, что при нажатии кнопки значение переменной stop будет изменено на 1, что вызовет break. Но значение не изменилось.

var stop = 0;
for(let i = 1; i <= 20; i++){
  
  if(stop === 1){
    break;
  }
  
  setTimeout(function(){
    $('ul').append('<li>'+ i +'</li>');
  },i * 500);
}

$('button').click(function(){
    stop = 1;
});
ul li{
  list-style-type: none;
  float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul></ul>

<br>
<button>stop</button>

Ответы [ 5 ]

0 голосов
/ 30 августа 2018

Я бы сделал это рекурсивно. Тогда у вас есть два условия останова, остановка и подсчет === макс.

var stop = false;

var print = function(count, max) {
   setTimeout(function(){
      if(stop){
         return;
      }else if(count === max){
         return;
      }else{
         $('ul').append('<li>'+ count +'</li>');
         return print(++count, max);
      }
   }, 500);
}

$('button').click(function(){
    stop = true;
});

print(0, 20);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul></ul>

<br>
<button>stop</button>
0 голосов
/ 30 августа 2018

Вы не можете остановить цикл for с помощью события click, поскольку JavaScript является однопоточным. Начало цикла

for(let i = 1; i <= 20; i++){
...

работает синхронно при выполнении без прерывания обработчиками событий .

Он инициирует 20 вызовов таймера, которые будут выполнены с интервалом 500 мс, и вернется в цикл обработки событий перед выполнением обработчика щелчка.

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

0 голосов
/ 30 августа 2018

Вы можете попробовать следующий код без jQuery. Я не рекомендую использовать --- $('ul').append('<li>'+ i +'</li>'); - это плохая практика. Верный путь - это создать узел перед циклом. в этой задаче вы можете использовать setIntrval вместо setTimout и loop

let itemList = (i) => {
    let item = document.createElement('li');
    item.value = i;
    item.textContent = i;
    return item;
}

let list = document.getElementById('list');
let stopButton = document.getElementById('stop');
let i = 0,
    interval;
let reset = () => {
    clearInterval(interval)
}

stopButton.addEventListener('click', () => {
    reset()
});

interval = setInterval(() => {
    list.append(itemList(i))
    i++;
    if (i > 20) reset()
}, 300)
<button id="stop">stop</button>
<br>

<ul id="list"></ul>
0 голосов
/ 30 августа 2018

Проблема в том, что весь цикл for был выполнен еще до того, как вы успели нажать кнопку. setTimeout на самом деле не спит и не блокирует, а помещает новое событие и просто продолжает цикл for.

Браузер, все его часы для целей тайм-аута и т. Д. Могут работать только после того, как ваш код на данный момент завершил работу. Нет функции, эквивалентной функции sleep ().

Возможно использование асинхронных функций (ES2015 IIRC, должно поддерживаться всеми текущими браузерами, отличными от IE), и ожидание обещания, созданного util.promisify(setTimeout)(2000), может быть более интуитивно понятным.

Функции блокировки отсутствуют. Там нет спящего внутри кода. Весь код в основном выполняется на ЦПУ и устанавливает события ввода-вывода и таймера, которые происходят асинхронно, как только синхронная часть уже завершена.

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

0 голосов
/ 30 августа 2018

Функции 20 setTimeout были вызваны еще до того, как вы нажали на стоп.
Один из многих способов исправить это - проверить, что переменная stop внутри функции setTimeout выполняется.

var stop = 0;
for (let i = 1; i <= 20; i++){
  setTimeout(function(){
    console.log(stop);
    if (stop !== 1) {
      $('ul').append('<li>'+ i +'</li>');
    }
  },i * 500);
}

$('button').click(function(){
    stop = 1;
});
ul li{
  list-style-type: none;
  float: left;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul></ul>

<br>
<button>stop</button>

Редактировать:

Вы спрашиваете, как остановить цикл for. Как это написано, вы не можете.
Вот как я бы это реализовал, чтобы «остановить» цикл с setTimeout.
Вы также можете сделать это с помощью setInterval (посмотрите ответ Сергей Петрашко , чтобы узнать как).
Читайте о различиях между ними здесь ):

var stop = false;

function addNumberAndCallNext(number, max) {
    if (!stop && number <= max) {
        $('ul').append('<li>'+ number +'</li>');
        setTimeout(addNumberAndCallNext.bind(null, ++number, max), 500);
    }
}

addNumberAndCallNext(1, 20);

$('button').click(function(){
    stop = true;
});
ul li {
  list-style-type: none;
  float: left;
  padding-right: 5px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul></ul>

<br>
<button>stop</button>
...