Динамическое обновление progress в интерфейсе во время работы цикла

Полезные скрипты
Ответить
mihas
Администратор
Сообщения: 1114
Зарегистрирован: 16:58, Ср, 18 авг 2004
Откуда: Москва
Контактная информация:

Динамическое обновление progress в интерфейсе во время работы цикла

Сообщение mihas » 20:29, Чт, 22 окт 2020

Обычные циклы яваскрипта типа for не позволяют обновить интерфейс в браузере до тех пор, пока сценарий не будет просчитан до конца.
Мне нужно было на тяжелую функцию мультиинтерполяции навесить progress, потому что функция может считать и по минуте, очень тяжелая математика, когда размерностей у интерполируемых данных не 2 а 5.

В результат не без помощи товарища сработала следующая функция. Здесь цикл for длительного выполнения у себя в коде я заменил на псевдоцикл function IterationFunction () и все сработало:

Код: Выделить всё

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta charset="UTF-8">
  
<script type="text/javascript">

function move() {
	
	//here calc before progress
	
	var iterationsPR = Math.random() * 1000;
	document.raz.rand.value = iterationsPR.toFixed(0);

    var progressMaxPR = iterationsPR;
    var progressPercentPR = 0;
    var counterPR = 0;

    (function IterationFunction () { //вместо цикла for
		
		//here first calc start at counterPR = 0, calc with dynamic progress
		
		counterPR++;

        progressPercentPR = (counterPR / progressMaxPR * 100).toFixed(1);
		document.getElementById("progressbar").value = progressPercentPR;
		
        if (counterPR < iterationsPR) {
            setTimeout(IterationFunction, 0);
        } else {
			
			//here next calc after progress and End function here
			
		} //else counter
    })(); //end counter, next is nothing
} //end function

</script>

<title>Progress bar</title>
</head>
<body>
<form name="raz">
<input type="button" onclick="move();" value="Click Me" />
<br /><br />
<label>Индикация выполнения: </label><progress id="progressbar" value="0" max="100" style="width:300px; height:8px;"></progress>
<br><br>
<input name="rand" type="text" value="0" size="5" /> random speed
</form>
</body>
</html>
Работающий код можно посмотреть по ссылке: https://jsfiddle.net/wzp34d90/
Обратите внимание на комменты по тексту, где мы вставляем те вычисления, которые нужны до активации progress, где размещаем код, работающий во время динамического обновления progress, и где размещаем код, который должен выполниться после окончания обновления progress.
Чтобы заменить цикл for на данный псевдоцикл, указываем конечное число цикла в переменной iterationsPR и начальное число цикла в переменной counterPR.
На скринах progress в реальном бою на странице https://cielab.xyz/spectralcalc_en.php
Вложения
progress в Firefox
progress в Firefox
progress в Chrome
progress в Chrome

mihas
Администратор
Сообщения: 1114
Зарегистрирован: 16:58, Ср, 18 авг 2004
Откуда: Москва
Контактная информация:

Re: Динамическое обновление progress в интерфейсе во время работы цикла

Сообщение mihas » 22:11, Чт, 04 фев 2021

Чуть позднее мне не понравилось, что при огромных массивах в десятки тысяч строк, а значит в десятки тысяч циклов, псевдоцикл с опросом интерфейса сам начинает тормозить процесс вычислений. Поэтому я по совету друга записал комбинацию из псевдоцикла и вложенного цикла. Смысл в следующем, к примеру у нас есть progress длиной 360 пикселов, то есть оптимально плавно обновить его достаточно 360 раз, чаще все равно бессмысленно. И есть на входе данные от нескольких строк до десятков тысяч строк, и число строк соответствует конечному значению цикла. Ну а дальше простая логика: я делю число строк на 360 пикселей, запускаю псевдоцикл всего 360 раз, а внутри него уже обычный цикл продолжает далее по счетчику выполнять обычные циклические операции без опроса интерфейса. То есть например, есть 3600 строк, значит внутри псевдоцикла обычный цикл посчитает по 10 строк и будет вызван 360 раз псевдоцклом, но всякий раз с обновленными данными по началу и концу цикла. Не буду утомлять вас кодом на яваскрипт, тем более что наверняка вы этот код еще лучше напишете для своей задачи. Идея думаю понятна: разбить один цикл на два со вложением одного в другой, где второй вложенный продолжает считать не с нуля, а с последнего сохраненного значения счетчика в первом псевдоцикле.
Таким образом расчеты для сверхбольших массивов происходят быстро и не тормозятся чрезмерно многократным опросом интерфейса. Этот опрос интерфейса можно сократить до количества заданных вами пикселей ширины прогресса, чтобы не потерять в плавности обновления и в скорости расчетов.

Ответить

Вернуться в «JavaScript»