Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Подвисает браузер на "тяжелой" задаче. (https://javascript.ru/forum/misc/83324-podvisaet-brauzer-na-tyazhelojj-zadache.html)

JobLack 17.11.2021 11:53

Подвисает браузер на "тяжелой" задаче.
 
Друзья, подскажите, пожалуйста.
Какой правильно подход использовать для разбиения "тяжелой" процедуры на подзадачи, как везде советуют . В таком коде браузер зависает с сообщением "окно не отвечает ...Подождать?"
...Promise.then((...) => {
// всякие преобразования... долго выполняемый код
})
Идеально на экран выдавать прогрессбар...но, как я понимаю, рендер будет ждать окончания "тяжелого" кода и потом что-то нарисует.
Спасибо.

ksa 17.11.2021 13:17

Цитата:

Сообщение от JobLack
Какой правильно подход использовать для разбиения "тяжелой" процедуры

Я такое просто "стартую" и сразу возвращаю некую "ссылку/ключ".
Потом с некой периодичностью опрашиваю сервер с этой "ссылкой/ключем" и получаю ответ типа "готово или нет" и "на каком этапе"...
Если готово - запрашиваю данные.
Если не готово - как-то отображаю "прогресс" на клиенте...

Aetae 17.11.2021 13:25

Тяжелый код сейчас можно пихать в webworker.
Вот тут недавно делал простой пример(содержимое не суть).

Либо разбивать ваши вычисления на куски, которые уже в свою очередь вызывать через Promise.then. Просто засунуть код в Promise.then никак вам не поможет, это не магия.

Alexandroppolus 17.11.2021 16:54

Цитата:

Сообщение от Aetae
Либо разбивать ваши вычисления на куски, которые уже в свою очередь вызывать через Promise.then

если в Promise.then, то всё равно будет подвисать, это же очередь микротасков. Надо в setTimeout

voraa 17.11.2021 17:47

Или requestIdleCallback, если не думать про Safari

JobLack 18.11.2021 01:15

Цитата:

Сообщение от ksa (Сообщение 541500)
Я такое просто "стартую" и сразу возвращаю некую "ссылку/ключ".
Потом с некой периодичностью опрашиваю сервер с этой "ссылкой/ключем" и получаю ответ типа "готово или нет" и "на каком этапе"...
Если готово - запрашиваю данные.
Если не готово - как-то отображаю "прогресс" на клиенте...

К сожалению это выполняется на клиенте. Апдейтится огромный JSON в цикле. На сервер это переносить - даже сразу не соображу, что это может затронуть.

JobLack 18.11.2021 01:18

Цитата:

Сообщение от voraa (Сообщение 541505)
Или requestIdleCallback, если не думать про Safari

С этим никогда не работал. Спасибо. Попробую. Идеально было бы - на несколько миллисекунд останавливать цикл, чтобы сделать промежуточный рендер.

Aetae 18.11.2021 04:07

сonst aLittle = () => new Promise(setTimeout);

for(let i = 0; i < length; i++) {
  // code
  if(i%1000 === 0) await aLittle();
}
=)

voraa 18.11.2021 08:02

Цитата:

Сообщение от JobLack
С этим никогда не работал. Спасибо. Попробую.

Если без всяких изысков, по простому, то как то так
// количество итераций, которое надо сделать
const N = 100500 ;
// гарантировано без тормозов можно сделать итераций
const NS = 100;  

let start = 0;

function workns () {
	const ne = Math.min(N, start+NS)
	let i;
	for (i = start; i< ne; i++) {
		/* тут делаем итерацию */
	}
	start = i;
}

function idleWork (time) {
	while (time.timeRemaining() > 0 && start < N) {
		workns()
	}
	if (start < N) requestIdleCallback(idleWork);
}

requestIdleCallback(idleWork);


Немного похоже на requestAnimationFrame, но есть возможность проверить( .timeRemaining() ) - сколько времени еще примерно есть

ksa 18.11.2021 08:29

Цитата:

Сообщение от JobLack
К сожалению это выполняется на клиенте. Апдейтится огромный JSON в цикле.

Принцип один и тот же...
Запускаешь в setTimeout()... Процесс пишет что-то о себе в "общедоступное место"...
Остается только с неким таймаутом мониторить "то место" и по готовности получить данные для использования.

ksa 18.11.2021 08:31

Цитата:

Сообщение от JobLack
Идеально было бы - на несколько миллисекунд останавливать цикл, чтобы сделать промежуточный рендер.

В моем варианте даже останавливаться не нужно... Просто забирай или отрабатывай готовые данные из "общедоступного места".

voraa 18.11.2021 08:59

Цитата:

Сообщение от ksa
Процесс пишет что-то о себе в "общедоступное место"...
Остается только с неким таймаутом мониторить "то место"

Процесс пишет. А мониторит кто? Второй процесс? А как в js без worker сделать второй процесс?

ksa 18.11.2021 10:40

Цитата:

Сообщение от voraa
А мониторит кто? Второй процесс?

Да.
Цитата:

Сообщение от voraa
А как в js без worker сделать второй процесс?

Еще один setTimeout()...

ksa 18.11.2021 10:44

voraa, рендеринг "по готовым данным" можно запускать и из процесса "обработчика".
Т.е. собственно мониторинг может только показывать "состояние дел". А сами изменения на клиенте может запускать процесс-обработчик некими "порциями".
Подготовил N-ное количество - запускай setTimeout(), который все N штук и отрисует...

Тут главное чтобы сам "источник данных" был доступен всем процессам-участникам.

voraa 18.11.2021 11:30

Цитата:

Сообщение от ksa
Еще один setTimeout()...

setTimeout не запускает новый процесс. Он просто ставит обработчик в очередь внутри одного процесса
Цитата:

Сообщение от ksa
А сами изменения на клиенте может запускать процесс-обработчик некими "порциями".
Подготовил N-ное количество - запускай setTimeout(), который все N штук и отрисует...

И зачем setTimeout, если есть requestIdleCallback?
Используя setTimeout, мы тупо пихаем очередное задание в очередь, не зная сильно ли она заполнена. Есть ли у браузера время обработать это задание не тормозя прорисовку. А requestIdleCallback выполняет задание именно тогда, когда браузер не занят обработкой событий, пересчетом стилей и прорисовкой. К тому же внутри задания можно узнать, осталось ли еще время, что бы посчитать очередную порцию.

ksa 18.11.2021 11:52

Цитата:

Сообщение от voraa
setTimeout не запускает новый процесс.

Это я условно так написал... :-?
Потому и указывал много чего в кавычках...

ksa 18.11.2021 11:53

Цитата:

Сообщение от voraa
И зачем setTimeout, если есть requestIdleCallback?

requestIdleCallback не всегда был в JS... :D
А коней на переправе не меняют.

Т.е. работает такой подход уже давно... А нужен он далеко не всегда.

ksa 18.11.2021 11:54

Цитата:

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

Я не критикую такой подход. :)
Я лишь описал как выходил их таких положений ранее сам.

ksa 18.11.2021 11:57

Цитата:

Сообщение от voraa
если есть requestIdleCallback?

Вот решил почитать про него...
https://developer.mozilla.org/ru/doc...stIdleCallback
И сразу там увидел большое сообщение
Цитата:

Experimental: Это экспериментальная технология

voraa 18.11.2021 12:12

Цитата:

Сообщение от ksa
requestIdleCallback не всегда был в JS...
А коней на переправе не меняют.

В JS не всегда были requestAnimationFrame, forEach, Map, Promise, async/await .... Всякие ?. ??
Это не повод ездить на старых клячах.

Цитата:

Сообщение от ksa
И сразу там увидел большое сообщение
Цитата:
Experimental: Это экспериментальная технология

Я же сразу написал, "если не думать про Safari" (ИЕ нашего времени)
Для Safari есть типа полифил (реализованный как раз через setTimeout).
Ну хоть в нормальных браузерах будет нормально работать.

ksa 18.11.2021 13:42

Цитата:

Сообщение от voraa
Это не повод ездить на старых клячах.

Запомни эти слова.
Их тебе, по прошествии времени, скажут молодые программисты. :D

voraa 18.11.2021 13:54

Цитата:

Сообщение от ksa
Их тебе, по прошествии времени, скажут молодые программисты.

Они и сейчас мне имеют права это сказать.
Я не знаю ни Vue, ни React.
И ваще, даже по новым законам, мне в следующем году на пенсию.

ksa 18.11.2021 14:04

Цитата:

Сообщение от voraa
мне в следующем году на пенсию

Тогда ты дюже резвый для пенса... :D

JobLack 18.11.2021 20:26

Цитата:

Сообщение от voraa (Сообщение 541528)
Если без всяких изысков, по простому, то как то так
// количество итераций, которое надо сделать
const N = 100500 ;
// гарантировано без тормозов можно сделать итераций
const NS = 100;  

let start = 0;

function workns () {
	const ne = Math.min(N, start+NS)
	let i;
	for (i = start; i< ne; i++) {
		/* тут делаем итерацию */
	}
	start = i;
}

function idleWork (time) {
	while (time.timeRemaining() > 0 && start < N) {
		workns()
	}
	if (start < N) requestIdleCallback(idleWork);
}

requestIdleCallback(idleWork);


Немного похоже на requestAnimationFrame, но есть возможность проверить( .timeRemaining() ) - сколько времени еще примерно есть


Ребят, спасибо. По моему в моей ситуации эту штуку проще всего реализовать...Классная вещь.. Спасибо.


Часовой пояс GMT +3, время: 16:06.