Javascript-PHP стеки, отложенное выполнение

Javascript стеки - это асинхронное взаимодейсвие php бекенда с js фронтендом. Вы устанавливаете в php бекенде js задачу(код), помещаете его в стек, устанавливая таймаут выполнения кода, и опционально ключ в стеке во избежание дублирования задачи. Код выполняется спустя заданный таймаут, удаляясь из стека и устанавливается следующий таймаут, если задачи ещё остались в стеке.

Синтаксис добавления задач в стек определён статическим методом StackJS::add($code, $timeout = 0, $key = '')

Выполнение и установка очередных таймаутов обеспечивается методом StackJS::execute() и запросами к /api/action/stackjs

При загрузке любой страницы выполняется метод StackJS::execute(), который добавляет в код страницы javascript, время выполнения которого в стеке истекло. При этом код удаляется из стека. А также, если в стеке ещё остались задачи, время которых не истекло, то в код страницы добавляется функция с самым минимальным таймаутом из стека задач, которая запросит /api/action/stackjs спустя этот таймаут. Этот запрос к api снова вернёт StackJS::execute() и т.д. пока стек не закончится.

Есть также отдельно метод StackJS::set() который не выполняет js код, а возвращает только функцию с таймаутом запроса к /api/action/stackjs

Итак, добавим на страницу

Wrong\Task\Stackjs::add('successToast("Js-Php стек");', 30, 'key-stack-test');
static add($code, $timeout=0, $key='')
Definition: Stackjs.php:25

И спустя 30 секунд после её загрузки увидим всплывающее сообщение. А если мы спустя 10 секунд уйдем на другую страницу, то там оно покажется уже через 20 секунд. А если мы перезагрузим эту страницу, до выполнения js кода из стека, то код и таймаут в стеке обновятся, и повторно, т.е. дважды данный код выполнен не будет, благодаря уникальному ключу key-stack-test. Если нужно изменить это поведение - просто не задавайте уникальный ключ элементу стека.

Посмотрите, что автоматически добавила нам система в страницу, увидев в стеке отложенную задачу:

То есть, для реализации, мы не делаем постоянных ежесекундных ajax запросов к бекенду, нагружая тем самым сервер, до состояния кипящего чайника. Кстати так делают именно чайники;) Мы делаем запросы тогда, и только тогда, когда они необходимы!

Наглядно проедмонстрируем это в цикле, на той же странице добавим:

for ($i = 0; $i < 33; $i++) {
Wrong\Task\Stackjs::add('successToast("Js-Php стек, итерация на ' . $i . ' секунде");', $i, $i);
}
if(empty($_POST['name'])) if(empty($_POST['type'])||!in_array($_POST['type'], ['page', 'modal', 'incode', 'select', 'action'])) $i

Посмотрите что добавилось при загрузке:

А затем обработчик /api/action/stackjs каждый последующий раз добавлял очередной код выполнения и очередной таймаут запроса к себе же, пока стек не закончился.

Пунктуально поздравим наших пользователей с Новым Годом, секунда в секунду(если он конечно будет на странице, а если у него сохранится сессия, то он увидит поздравление позже):

Wrong\Task\Stackjs::add('successToast("С Новым Годом!");', strtotime('first day of January next year') - time(), 'new-year');
setcookie('FROM_UID', $uid, [ 'expires'=> time()+31536000, 'path'=> '/', 'domain'=> $_SERVER['HTTP_HOST'], 'secure'=> Wrong\Start\Env::$e->IS_SECURE, 'httponly'=> false, 'samesite'=> Wrong\Start\Env::$e->IS_SECURE ? 'None' :'Lax']) or setcookie('FROM_UID' time()+31536000
Definition: from-user.php:36

Усложняем задачи. Пример простейшей рекурсии, которая размещается и стартует при запросе к /api/action/my-any-action выполняясь каждые 5 секунд:

<?php
Wrong\Task\Stackjs::add('successToast("Рекурсивный Js-Php стек");$.getScript('/api/action/my-any-action');', 5, 'key-stack-test');
exit(Wrong\Task\Stackjs::set());
if(($dbh=Connect::getInstance(true) ->dbh) && $dbh->query("SHOW TABLES") ->fetchAll() && $dbh->query("SELECT COUNT(*) FROM `users`") ->fetchColumn()) if(!empty($_POST)) exit
Definition: install.php:198

Теперь нам достаточно лишь однажды вызвать javascript действие /api/action/my-any-action и код будет выполняться на сайте бесконечно, пока мы не убьем свою сессию

<script>
$.getScript('/api/action/my-any-action');
</script>

Но давайте уже будем работать с wrong-mvc как положено, ведь у нас есть специальные триггеры действий, которые активируют тоже самое поведение:

<a class="btn btn-primary" data-action="my-any-action" data-response="script">Запустить рекурсию!</a>

или же с подтверждающим окошком

<a class="btn btn-primary" data-action="my-any-action" data-response="script" data-header="Запустить рекурсию?" data-body="Когда надоест, выйдите вон из своей сессии.">Запустить рекурсию!</a>

Вот ещё пример применения на странице:

Wrong\Task\Stackjs::add('successToast("Я выполняюсь моментально без задержек! Но я вижу что ещё там в стеке кое что ниже есть через 3 секунды, поставлю ещё таймаут 3 секунды и вызову api когда придёт это время");', 0, 'key1');
Wrong\Task\Stackjs::add('_modal("#error", null, "error=Время пришло, api запрошено. Но я вижу там ещё кое что есть, через 4 сек, поставлю ещё таймаут на 4 секунды");', 3, 'key2');
Wrong\Task\Stackjs::add('$("#error").modal("hide");successToast("Окно скрыто. А что там, есть ещё что то в стеке?");', 7, 'key3');
Wrong\Task\Stackjs::add('dangerToast("Да, есть я в стеке! Но меня ждать ещё 23 секунды. Если дождёшься - я исполнюсь, а если перейдешь на другие страницы, то я всё равно исполнюсь, но ровно в свой срок!");', 30, 'key4');