Остановка ajax запросов

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
sanchez812
Сообщения: 2
Зарегистрирован: 2020.01.27, 09:24

Остановка ajax запросов

Сообщение sanchez812 »

Добрый день.

Суть задачи следующая:
У пользователя есть небольшая форма для выгрузки отчётов, он заполняет её и посылает ajax запросом. По выполнении приходит ответ (либо таблица на страницу пользователя, либо отдаётся файл, не важно).
Отчёты (обычно это один sql запрос) могут быть достаточно тяжёлыми, выгрузка некоторых может занимать много минут, и это нормально.
Суть в том, что во время выгрузки у пользователя должна быть возможность остановить отчёт. То есть нужно как минимум убить sql (php скрипт при этом сам вылетит). Простое abort() тут не катит - оно не остановит ни php, ни sql (что нужно, чтобы sql не загружал базу и не висел по 10 раз).

На обычном php это сделано довольно легко: php скрипт отчёта при старте (при помощи ob_flush и ему подобного, точный код могу привести, если нужно) отдаёт клиенту pid своего соединения с бд, и клиент при остановке отчёта просто другим аяксом делает килл этому процессу. Sql остановлен, php вылетает, аякс отчёта абортится, можно запускать снова.

При переносе такого на Yii2 столкнулся с рядом проблем:
1. Не работает промежуточный вывод.
2. Не работают несколько аяксов к yii2.

Первую проблему решил немного костыльным методом: Клиент на старте отчёта генерит уникальный "ключ" и отдаёт его серверу. Тот инсертит этот ключ вместе со своим pid в специальную таблицу в базе и начинает выполнять потенциально тяжёлый запрос отчёта. При остановке другой аякс должен забрать из базы pid по соответствию "ключа" и грохнуть его. Это работает.

НО! Я не могу послать второй аякс на остановку пока не выполнится первый. Более того, пока не выполнится первый аякс, я не могу даже открыть этот сайт в другой вкладке или просто обновить страницу (точнее, yii ждёт, пока выполнится первый аякс запрос, но не дожидается, и дело не в обращении к одному и тому же контроллеру), и команда abort не помогает.
Как решить эту проблему?

Я могу, конечно, написать тот скрипт, который стопает запросы, не на yii2. И это наверняка сработает. Но это слишком уж костыльно.

Как обойти вторую проблему? Неужели разработчики yii2 не придумали ничего для остановки ajax запросов от клиента (все попытки нагуглить подобное выдавали абсолютно не нужные результаты)?
azz
Сообщения: 197
Зарегистрирован: 2016.07.06, 17:20

Re: Остановка ajax запросов

Сообщение azz »

У вас есть некое непонимание того, как работают аякс-запросы.

Что касается невозможности нескольких запросов подряд при незавершенных предыдущих. Фреймворк тут не при чем, я вообще не понимаю как вы хотите запустить аяксом некий длинный скрипт и при этом сразу получить от него ответ. Аякс-запрос так не работает, он должен либо получить ответ либо отвалится по таймауту.

Так же аякс-запрос не будет работать со всякими функциями буферизации ввода-вывода.

Касательно вашей задачи. Я решал подобную следующим образом.
Есть 3 файла: hi_load.php, ajax.php и index.html.

В index.html у нас есть форма с двумя кнопками "старт" и "стоп". Там есть индикатор прогресса, который через js таймер, скажем, раз в 5 секунд дёргает ajax.php, который в свою очередь просто мониторит некое поле в бд (в файле, сессии), в котором записано текущее состояние (работает/бездействует) и прогресс. Результат записывается в какой нибудь div.

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

hi_load.php в процессе работы пишет статус и прогресс в бд (в файл, сессию, redis, это смотрите сами). Я бы использовал для этого сессию пользователя.

По нажатии кнопки "стоп" у ajax.php есть два пути.
1) Если у вас один долгий запрос, то именно ajax.php должен убить его по ранее записанному pid в той же сессии юзера.
2) Если у вас просто долгое выполнение, то по нажатии кнопки "стоп" ajax.php просто пишет в сессию слово "стоп", например. А hi_load.php в каких-то местах должен проверять значение поля остановки, и если там есть "стоп", то нужно делать exit. Если нужно убить какие-то запросы, воспользуйтесь register_shutdown_function. При этом, если у нас цикл, состоящий из миллионов итераций можно делать нечто вроде

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

for ($i = 0; $i < 1000000; $i++) {
	if (($i % 100000) === 0) {
		exit;
	}
}
что бы не повесить сервер миллионом проверок :)

Сделать всё это на Yii проще простого, т.к. многие вещи уже готовы и их не нужно писать с нуля.
Ответить