php асинхронное выполнение скрипта

Асинхронное выполнение PHP

php асинхронное выполнение скрипта. soc facebook red. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-soc facebook red. картинка php асинхронное выполнение скрипта. картинка soc facebook red. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно. php асинхронное выполнение скрипта. soc twitter red. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-soc twitter red. картинка php асинхронное выполнение скрипта. картинка soc twitter red. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно. php асинхронное выполнение скрипта. soc telegram red. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-soc telegram red. картинка php асинхронное выполнение скрипта. картинка soc telegram red. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Выбор асинхронного решения всегда нетривиален, особенно в случае выполнения PHP.

PHP-FPM fastcgi_finish_request()

Если в качестве веб-сервера используется Nginx, то к вашим услугам отличный менеджер процессов FastCGI (FPM).

php асинхронное выполнение скрипта. smngoz4p6q5d38ig7.0bd156f3. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-smngoz4p6q5d38ig7.0bd156f3. картинка php асинхронное выполнение скрипта. картинка smngoz4p6q5d38ig7.0bd156f3. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

К примеру, отправка почты в асинхронном режиме будет выглядеть так:

echo ‘Подтверждение отправлено на почту’;

mail($to, ‘Подтверждение’, ‘Это ваш ящик?’);**

echo ‘Введены не все данные’;

## После вызова fastcgi_finish_request() посетитель сразу увидит ответ от сервера

Больше настроек и примеров в материале Асинхронность в PHP и FPM.

Неблокирующий режим потока

По умолчанию сокет открывается в блокирующем режиме, то есть дожидается полного соединения, прежде чем возвратить значение. Так что для выполнения асинхронных вызовов пригодится дополнительная директива socket_set_nonblock :

function multiHTTP ($urlArr) <

$sockets = Array(); // массив сокетов

for ($x=0;$x
## Функция подключается к массиву URL-ов в асинхронном режиме и возвращает массив результатов

Небольшой пример использования:

$data = fread($fp, 8192);

>
## Вызов url и моментальный возврат ответа, удобно для запуска других скриптов

Exec и cURL

Для запуска фоновых процессов curl можно использовать exec. Простой пример выполнения будет выглядеть так:

## Запрос выполняется почти моментально (50 мс), а в фоне запускается нужный длинный скрипт

Процессы curl также можно запустить из под командной оболочки:

‘https://api.somesite.com/import’ > /dev/null 2>&1 &

## Запуск и раздвоение скрипта

Расширение pthreads

Для PHP есть многопоточное расширение [http://pthreads.org/ pthreads].

php асинхронное выполнение скрипта. shpzkl771k4767jns.42f2531a. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-shpzkl771k4767jns.42f2531a. картинка php асинхронное выполнение скрипта. картинка shpzkl771k4767jns.42f2531a. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Оно не входит в ядро PHP, так что его нужно установить:

pecl install pthreads
## Расширение доступно в репозитории PECL и совместимо с ZTS

Простейший пример для проверки работоспособности расширения будет выглядеть так:

public function run()<

?>
## Вывод номеров воркеров в 8 потоков

Использование сервера очередей

Если веб-приложение большое, то логичнее воспользоваться сервером очередей, который даст больше возможностей и функций.

php асинхронное выполнение скрипта. smngoz2463e8oduq9g.f3e16682. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-smngoz2463e8oduq9g.f3e16682. картинка php асинхронное выполнение скрипта. картинка smngoz2463e8oduq9g.f3e16682. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Например Gearman, который содержит множество клиентов, в том числе для PHP. С его помощью достаточно легко реализовать асинхронные операции, как отправку почты.

Самое главное

Выбор метода асинхронного выполнения полностью зависит от ваших задач. Но самыми производительными и надежными будут решения с использованием модуля PHP-FPM и сервера очередей.

Источник

Асинхронное выполнение PHP скрипта на подпроцессах

php асинхронное выполнение скрипта. cf74af71b5b74c3ca00c8eaacc5cdb26. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-cf74af71b5b74c3ca00c8eaacc5cdb26. картинка php асинхронное выполнение скрипта. картинка cf74af71b5b74c3ca00c8eaacc5cdb26. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Добрый день, уважаемые хабровчане.

Сегодня я хотел бы поговорить о таких нетривиальных вещах, как асинхронные (параллельные) расчеты в языке PHP.
Сам по себе PHP — это скриптовый язык, который никогда и не претендовал на многопоточность. Но чем дальше в лес, тем более серьезные задачи стоят перед разработчиками, и тем больше приходится «извращаться» с пыхом, потому что мигрировать на более приспособленный под эти задачи язык программирования многие компании попросту боятся и не хотят. Следовательно, приходится работать с тем, что дают.
Подробности под катом…

Какое-то время назад передо мной стояла достаточно нетривиальная задача.
Если вкратце, то в проекте было реализовано примерно 20 очень тяжеловесных модулей по расчету стоимости товара.
Всё это висело на нескольких реляционных таблицах, каждый из модулей содержал свои собственные правила расчета и тп. Но выдавать на клиент всё это нужно было единым пакетом. И это должно было выполняться быстро. Очень быстро. Кеширование спасало, но в очень ограниченных объемах, совсем недостаточных для выполнения технических требований.

Алгоритм был довольно прост: на вход подавались необходимые аргументы, потом инстанцировались в массив все модули, и в цикле всё это дело просчитывалось. Ответ собирался в единый объект и выплёвывался на клиент для постобработки.

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

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

К сожалению, так в итоге ни к чему и не пришли. Было решено свернуть проект.

Но для меня вопрос остался открыт, потому что решение быть должно. И ещё тогда мы задумывались о некоем подобии “подпроцессов”, которые порождает основной скрипт (аналог exec() функции).

С тех пор прошло довольно много времени, из проекта я давно ушел. Но вот буквально на прошлой неделе у меня появилась одна очень нетривиальная задача: написать скрипт, который определенным образом залогирует текущее состояние некоей entity и часть её тяжелых реляционных зависимостей. Для этого используется 2 класса, правильно подготавливающих данные и сохраняющих это в БД. Проблема в том, что таких объектов примерно 2800. Мой скрипт отваливается по

На каждый пакет из 50 entities тратится, в среднем, 190мб памяти, с каждым новым пакетом кол-во использованной памяти росло. При полном отключении ограничений на использование оперативки, я получил такую же ошибку плюс Segmentation Fault.

Т.е. так или иначе, нужно было придумать как избежать переполнения оперативной памяти в скрипте, и постараться сделать его “чуточку” побыстрее. Сперва попытались разобраться, почему увеличивается потребление памяти из итерацию в итерацию. Оказалось, что ноги растут из особенностей работы симфового ServiceContainer и EventDispatcher. Там в event подпихивается весь контейнер, и потом это делается рекурсивно. Обходить нам это всё было, честно говоря, лень, и мой коллега предложил довольно изящное решение.

В наборе компонентов Symfony2 есть такая замечательная штука, как Symfony Process Component.
Эта вундервафля позволяет в ходе выполнения скрипта породить подпроцесс и запустить его в CLI-режиме (как обычную консольную команду).

Сперва мы просто попробовали “отпочковывать” по одному процессу для ограничения использования RAM. Но потом в доках вычитали, что эта штука умеет работать асинхронно.

Было решено опробовать это в деле. В итоге получилось нечто вроде этого(Ниже пример с Example-репозитория на GitHub. Логика самих подпроцессов очень простая, но утяжеленная):

В итоге имеем примерно вот такую картину:
php асинхронное выполнение скрипта. image loader. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-image loader. картинка php асинхронное выполнение скрипта. картинка image loader. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Скажу откровенно, я был очень впечатлён такими возможностями.
Надеюсь, некоторым эта статья будет в помощь. В качестве дополнительного материала оставлю тут ссылку на репозиторий, где был реализован пример, приведенный выше.

Спасибо за внимание. Буду рад отзывам и комментариям.

Обновил скриншот htop. Теперь есть данные по процессам. Спасибо hell0w0rd

Источник

Асинхронный PHP. Зачем?

php асинхронное выполнение скрипта. vrydzcqx0ogo5vfy a2uzf0wmgs. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-vrydzcqx0ogo5vfy a2uzf0wmgs. картинка php асинхронное выполнение скрипта. картинка vrydzcqx0ogo5vfy a2uzf0wmgs. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Асинхронное программирование сегодня востребовано, особенно в веб-разработке, где отзывчивость приложения особенно важна. Никому не хочется ждать, пока приложение «отвиснет», пусть даже в это время оно выполняет запрос к базе данных, отправляет электронное письмо или работает над другими задачами, которые могут занять много времени. Клиент хочет получить ответ на свое действие, лучше всего — сразу же. Если ваше приложение работает медленно, вы теряете клиентов. Столкнувшись с зависающим приложением, чаще всего пользователь просто закрывает его и уже никогда не возвращается. С точки зрения пользователя, приложение просто зависло, он не может понять, почему это произошло: выполняет ли оно сложную операцию или перестало работать в принципе.

Представляем перевод статьи бэкенд-разработчика Skyeng Сергея Жука.

Отзывчивость приложения

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

Параллелизм и асинхронность

Когда люди видят асинхронный код, чаще всего они сразу же думают: «Отлично, я могу распараллелить процессы!». Возможно, я вас разочарую, но на самом деле это не так. Асинхронность и параллелизм — не одно и то же. Уловить эту разницу может быть непросто, поэтому давайте попробуем разобраться.

Если задачи выполняются асинхронно, они не блокируют друг друга и выполнение одной задачи не зависит от завершения другой. Параллелизм, в свою очередь, подразумевает запуск нескольких отдельных задач одновременно как независимых единиц работы.

Идите и выполните задачу самостоятельно. Дайте мне знать, когда закончите, и покажите результат. В это время я могу продолжать заниматься своей задачей.

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

php асинхронное выполнение скрипта. image loader. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-image loader. картинка php асинхронное выполнение скрипта. картинка image loader. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.
Чтобы понять разницу между асинхронным и параллельным выполнением на реальном примере, сравним два популярных веб-сервера: Apache и Nginx. Они прекрасно иллюстрируют это различие. Nginx использует асинхронную модель на основе событий в то время как Apache задействует параллельные потоки. Apache создает новые потоки для каждого дополнительного соединения, поэтому здесь максимальное число допустимых соединений зависит от объема доступной памяти в системе. Когда предел подключений достигнут, Apache перестает создавать дополнительные подключения. Ограничивающим фактором при настройке Apache является память (помните, что параллельное выполнение часто зависит от железа). Если поток останавливается, клиент ожидает ответа, пока поток не освободится и не сможет отправить ответ.

Nginx работает не так, как Apache, — он не создает новых потоков для каждого входящего запроса. У Nginx есть основной рабочий процесс (или несколько воркеров, зачастую для одного процессора имеется один рабочий процесс ), который является однопоточным. Этот воркер может обрабатывать тысячи соединений «одновременно» и делает это асинхронно, одним потоком, а не параллельно в несколько потоков.

Таким образом, «асинхронность» — это то, каким образом мы выстраиваем систему, это композиция не зависящих друг от друга процессов. Под «параллельным выполнением» понимают выполнение нескольких процессов в один и тот же момент времени, при этом они могут быть связаны или не связаны между собой. При асинхронном выполнении мы обрабатываем несколько задач сразу, а при параллельном выполнении — запускаем несколько процессов сразу. Может показаться, что это одно и то же, но это не так. Асинхронность описывает структуру, параллельность — способ выполнения.

Асинхронное выполнение можно сравнить с устройствами ввода-вывода на вашем компьютере (мышь, клавиатура, дисплей). Все они управляются операционной системой, но каждое из них — независимая часть ядра. Эти процессы идут асинхронно, они могут быть параллельными, но это не обязательно. Таким образом, чтобы обеспечить согласованность, вы должны создать связь между этими независимыми частями, чтобы координировать их.

А что от этого бэкенду?

Вы можете сказать, что на бэкенде отзывчивость не так уж и важна. Все эти крутые асинхронные штуки с JavaScript происходят на фротенде, а ваш сервер просто отвечает на запросы, поэтому за отзывчивость приложения должен отвечать фротенд, но никак не вы. Да, это правда, но задача сервера не ограничивается только ответами API. Иногда приходится управлять сложными задачами, например, загрузкой видео. В этом случае, возможно, отзывчивость не является ключевым фактором, но ее отсутствие приводит к расходованию ресурсов впустую, потому что приложению приходится простаивать. Оно может ожидать завершения операций файловой системы, сетевого взаимодействия, запросов к базе данных и тому подобное. Часто эти операции ввода-вывода выполняются очень медленно по сравнению с вычислениями процессора, например когда мы конвертируем видеофайлы. И пока мы медленно сохраняем или читаем файл, процессор вынужден находиться в режиме ожидания вместо того, чтобы выполнять полезную работу. Как мы уже выяснили, вместо ожидания мы можем запускать эти задачи в фоновом режиме. Как? Идем дальше.

Асинхронный PHP

В JavaScript прямо из коробки доступна встроенная поддержка и решения для написания асинхронного кода. Также есть Node.js, который позволяет писать асинхронные серверные приложения. В JavaScript мы можем использовать функцию setTimeout () для демонстрации примера асинхронного кода:
php асинхронное выполнение скрипта. image loader. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-image loader. картинка php асинхронное выполнение скрипта. картинка image loader. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

При запуске этого кода мы увидим следующее:
php асинхронное выполнение скрипта. image loader. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-image loader. картинка php асинхронное выполнение скрипта. картинка image loader. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Функция setTimeout () отправляет код в очередь и выполнит его по завершению текущего стека вызовов. Это означает, что мы нарушаем синхронный поток кода и задерживаем выполнение. Второй вызов console.log () выполняется перед вызовом в очереди внутри вызова setTimeout ().

Что же насчет PHP? В PHP у нас нет подходящих адаптированных инструментов для написания по-настоящему асинхронного кода. Не существует эквивалента setTimeout (), и мы не можем просто отложить или поставить в очередь выполнение какого-либо кода. Вот почему стали появляться такие фреймворки и библиотеки, как Amp и ReactPHP. Их идея заключается в том, чтобы скрыть низкоуровневые детали языка и дать пользователю инструменты высокого уровня, которые позволяют писать асинхронный код и управлять конкурентным выполнением процессов, как JavaScript и Node.js.

Зачем мне использовать PHP, если есть Node.js и Go?

Этот вопрос часто возникает, когда речь идет об асинхронном PHP. Почему-то многие выступают против использования PHP для написания асинхронного кода. Кто-то всегда предлагает вместо PHP использовать Go или Node.js.

Твит assertchris прекрасно описывает такие ситуации:

php асинхронное выполнение скрипта. image loader. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-image loader. картинка php асинхронное выполнение скрипта. картинка image loader. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Конечно, когда PHP только создавался, не было цели сделать его языком программирования, который можно использовать для создания больших сложных приложений. В то время никто не думал ни о JavaScript, ни об асинхронности. Но теперь у нас есть совершенно другой PHP, который уже имеет собственные функции для написания асинхронного кода (например, функцию stream_select ()).

Да, вы можете использовать Go или Node.js для создания асинхронных серверных приложений, но это не всегда решает проблему. Если у вас уже есть большой опыт работы с PHP, вам будет гораздо проще просто изучить библиотеки и инструменты, подходящие для вашей ситуации, чем изучать новый язык и новую экосистему. Такие инструменты, как ReactPHP или Amp, позволяют писать асинхронный код так же, как вы делаете это в Node.js. Эти инструменты достаточно развиты и имеют стабильные версии, поэтому вы можете безопасно использовать их в продакшене.

Не только CLI

Я не собираюсь писать чат, сервер или что-то подобное. Я просто хочу ускорить работу сайта.

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

Или вам нужно сделать несколько внешних вызовов API – например, когда пользователь завершает платеж, вы хотите отправить электронное письмо или push-уведомление. Вы можете выполнить эти вызовы API асинхронно, а затем продолжить свой традиционный синхронный поток кода. Нет необходимости целиком переписывать приложение и удалять все, что замедляет работу. Просто определите узкие места, которые мешают производительности, и, вполне возможно, их получится исправить с помощью асинхронного кода.

Да, асинхронный код в большинстве случаев все еще используется в сценариях CLI, но он не ограничивается real-time чатами и серверами. Если вы просто хотите ускорить работу своего сайта, вам не нужно отказываться от вашего фреймворка Symfony или Laravel и создавать свой собственный полностью асинхронный сервер приложений.

Заключение

Не бойтесь изучать новую парадигму. PHP – это гораздо больше, чем «запусти скрипт, выполни код и умри». Вы удивитесь, когда поймете, что можно использовать знакомый вам PHP совершенно по-новому, так, как вы никогда его не использовали! Асинхронный код и событийно-ориентированное программирование расширит ваши представления о PHP и возможности использования этого языка. Не нужно изучать новый язык для написания асинхронных приложений только потому, что «PHP — неподходящий инструмент» или «я всегда так делал, его невозможно улучшить». Просто попробуйте!

Ну а также напоминаем, что у нас всегда много интересных вакансий для разработчиков!

Источник

Управляем асинхронностью в PHP: от промисов к корутинам

php асинхронное выполнение скрипта. udaht9uat9o8n1cj2y uwpzhbqw. php асинхронное выполнение скрипта фото. php асинхронное выполнение скрипта-udaht9uat9o8n1cj2y uwpzhbqw. картинка php асинхронное выполнение скрипта. картинка udaht9uat9o8n1cj2y uwpzhbqw. Для ускорения работы программы широко применяется практика асинхронного выполнения задач. Это значит, что операции выполняются непоследовательно, отложенно.

Что такое асинхронность? Если кратко, то асинхронность означает выполнение нескольких задач в течение определенного промежутка времени. PHP выполняется в одном потоке, что означает, что в любой момент времени может выполняться только один фрагмент PHP-кода. Это может показаться ограничением, но на самом деле предоставляет нам большую свободу. Нам в итоге не приходится сталкиваться со всей той сложностью, которая связана с многопоточным программированием. Но с другой стороны, здесь есть свой набор проблем. Нам приходится иметь дело с асинхронностью. Нам нужно как-то управлять ей и координировать ее.

Представляем перевод статьи из блога бэкенд-разработчика Skyeng Сергея Жука.

Например, когда мы выполняем два параллельных HTTP-запроса, мы говорим, что они «выполняются параллельно». Обычно это легко и просто сделать, однако проблемы возникают, когда нам нужно упорядочить ответы этих запросов, например, когда один запрос требует данные, полученные от другого запроса. Таким образом, именно в управлении асинхронностью и заключается наибольшая трудность. Есть несколько различных способов решить эту проблему.

В настоящее время в PHP нет встроенной поддержки высокоуровневых абстракций для управления асинхронностью, и нам приходится использовать сторонние библиотеки, такие как ReactPHP и Amp. В примерах этой статьи я использую ReactPHP.

Промисы

Чтобы лучше понять идею промисов, нам пригодится пример из реальной жизни. Представьте себе, что вы в «Макдоналдсе» и хотите сделать заказ. Вы платите за него деньги и таким образом начинаете транзакцию. В ответ на эту транзакцию вы ожидаете получить гамбургер и картошку фри. Но кассир вам не возвращает сразу еду. Вместо этого вы получаете чек с номером заказа. Считайте этот чек промисом для будущего заказа. Теперь вы можете взять этот чек и начать думать о своем вкусном обеде. Ожидаемые гамбургер и картошка фри еще не готовы, поэтому вы стоите и ждете, пока ваш заказ не будет выполнен. Как только его номер появится на экране, вы обменяете чек на свой заказ. Это и есть промисы:

Промис — это представление для будущего значения, независимая от времени оболочка, которую мы оборачиваем вокруг значения. Нам неважно, значение уже здесь или его еще нет. Мы продолжаем думать о нем одинаково. Представьте, что у нас есть три асинхронных HTTP-запроса, которые выполняются «параллельно», так что они будут завершены примерно в один момент времени. Но мы хотим каким-то образом скоординировать и упорядочить их ответы. Например, мы хотим вывести эти ответы, как только они будут получены, но с одним небольшим ограничением: не печатать второй ответ до тех пор, пока не будет получен первый. Здесь я имею в виду, что если $promise1 выполняется — то мы печатаем его. Но если $promise2 выполняется первым, мы его не печатает, потому что $promise1 еще в процессе выполнения. Представьте, что мы пытаемся адаптировать три конкурентных запроса таким образом, что для конечного пользователя они выглядят как один быстрый запрос.

Итак, как же мы можем решить такую проблему с помощью промисов? Прежде всего нам нужна функция, которая возвращает промис. Мы можем собрать три таких промиса, а затем скомпоновать их вместе. Вот некий фейковый код для этого:

Из клиентского кода мы просто вызываем функцию makeRequest() и получаем промисы:

Это было просто, но теперь нам нужно как-то упорядочить эти ответы. Еще раз, мы хотим, чтобы ответ из второго промиса был напечатан только после завершения первого. Чтобы решить эту задачу, можно построить цепочку из промисов:

В приведенном выше коде мы начинаем с $promise1. Как только он будет выполнен, мы печатаем его значение. Нам неважно, сколько это займет времени: меньше секунды или час. Как только промис будет выполнен, мы напечатаем его значение. А затем мы ждем $promise2. И здесь у нас может быть два сценария:

$promise2 уже завершен, и мы сразу печатаем его значение;
$promise2 еще выполняется, и мы ждем.

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

Вот так можно управлять асинхронностью с помощью промисов. И это выглядит замечательно, цепочка промисов гораздо симпатичнее и понятнее, чем куча вложенных колбэков.

Генераторы

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

Мы можем буквально локально приостановить работу генератора, чтобы дождаться выполнения промиса. Основная идея в том, чтобы использовать промисы и генераторы вместе. Управление асинхронностью они полностью берут на себя, а мы же просто вызываем yield, когда нам нужно приостановить генератор. Вот та же программа, но теперь мы соединяем генераторы и промисы:

Для этого кода я использую библиотеку recoilphp/recoil, которая позволяет вызвать ReactKernel::start(). Recoil дает возможность использовать генераторы PHP для выполнения асинхронных промисов ReactPHP.

Здесь мы по-прежнему «параллельно» выполняем три запроса, однако теперь мы упорядочиваем ответы с помощью ключевого слова yield. И снова выводим результаты по окончании каждого промиса, но только после выполнения предыдущего.

Корутины

Корутины — это способ разделения операции или процесса на чанки, с некоторым выполнением внутри каждого такого чанка. В результате получается, что вместо выполнения всей операции за раз (что может привести к заметному зависанию приложения), она будет выполняться постепенно, пока не будет выполнен весь необходимый объем работы.

Теперь, когда у нас есть прерываемые и возобновляемые генераторы, мы можем использовать их для написания асинхронного кода с промисами в более привычном для нас синхронном виде. С помощью генераторов PHP и промисов можно полностью избавиться от колбэков. Идея состоит в том, что когда мы отдаем промис (с помощью вызова yield), корутина подписывается на него. Корутина приостанавливается и ждет, пока промис не будет завершен (выполнен или отменен). Как только промис будет завершен, корутина продолжит свое выполнение. При успешном выполнении промиса корутина отправляет полученное значение обратно в контекст генератора, используя вызов Generator::send($value). Если промис фейлится, то корутина кидает исключение через генератор, используя вызов Generator::throw(). При отсутствии колбэков мы можем писать асинхронный код, который выглядит почти как привычный нам синхронный.

При использовании корутин порядок выполнения в асинхронном коде теперь имеет значение. Код выполняется точно до того места, где происходит вызов ключевого слова yield и затем приостанавливается, пока промис не будет завершен. Рассмотрим следующий код:

Здесь будет выведено Promise1:, затем выполнение приостанавливается и ждет. Как только промис из makeRequest(‘url1’) будет завершен, мы выводим его результат и переходим к следующей строчке кода.

Стандарт промисов Promises/A+ гласит, что каждый промис содержит методы then() и catch(). Такой интерфейс позволяет строить цепочки из промисов и опционально ловить ошибки. Рассмотрим такой код:

Здесь у нас есть цепочка промисов, передающая результат каждого предыдущего промиса в следующий. Но в этой цепочке отсутствует блок catch(), здесь нет обработки ошибок. Когда какой-либо промис в цепочке фейлится, выполнение кода переходит к ближайшему в цепочке обработчику ошибок. В нашем же случае это означает, что невыполненный промис будет проигнорирован, а любые выброшенные ошибки пропадут навсегда. С корутинами обработка ошибок выходит на первый план. Если какая-либо асинхронная операция завершится неудачей, будет выброшено исключение:

Делаем асинхронный код читабельным

Генераторы имеют действительно важный побочный эффект, который мы можем использовать для управления асинхронностью и который позволяет решить проблему читабельности асинхронного кода. Нам тяжело понять, как будет выполняться асинхронный код из-за того, что поток выполнения постоянно переключается между различными частями программы. Однако наш мозг в основном работает синхронно и однопоточно. Например, мы планируем наш день весьма последовательно: сделать одно, затем другое и так далее. Но асинхронный код работает не так, как наш мозг привык думать. Даже простая цепочка промисов может выглядеть не очень читабельно:

Приходится мысленно разобрать ее, чтобы понять, что там происходит. Таким образом, нам нужен другой паттерн для управления асинхронностью. И коротко говоря, генераторы предоставляют способ, позволяющий писать асинхронный код так, чтобы он выглядел как синхронный.

Промисы и генераторы объединяют лучшее из обоих миров: мы получаем асинхронный код с большой производительностью, но при этом он выглядит как синхронный, линейный и последовательный. Корутины позволяют скрыть асинхронность, которая становится уже деталью реализации. А наш код при этом выглядит так, как привык думать наш мозг — линейно и последовательно.

Если мы говорим о ReactPHP, то для записи промисов в виде корутин можно использовать библиотеку RecoilPHP. В Amp корутины доступны сразу из коробки.

Источник

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *