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

 

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

Автор: admin от 24-05-2019, 22:40, посмотрело: 417

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

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



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

recoilphp/recoil, которая позволяет вызвать ReactKernel::start(). Recoil дает возможность использовать генераторы PHP для выполнения асинхронных промисов ReactPHP.[/i][/quote]

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



Корутины



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



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



Последовательное исполнение



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



<?php

use RecoilReactReactKernel;

// ...

ReactKernel::start(function () {
    echo 'Response 1: ', yield makeRequest('url1'), PHP_EOL;
    echo 'Response 2: ', yield makeRequest('url2'), PHP_EOL;
    echo 'Response 3: ', yield makeRequest('url3'), PHP_EOL;
});


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



Обработка ошибок



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



<?php

operation()then(function ($result) {
    return anotherOperation($result);
})then(function ($result) {
    return yetAnotherOperation($result);
})then(function ($result) {
    echo $result;
});


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



<?php

use RecoilReactReactKernel;
use ReactPromiseRejectedPromise;

// ...

function failedOperation() {
    return new RejectedPromise(new RuntimeException('Something went wrong'));
}

ReactKernel::start(function () {
    try {
        yield failedOperation();
    } catch (Throwable $error) {
        echo $errorgetMessage() . PHP_EOL;
    }
});


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



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



<?php

$promise1
    then('var_dump')
    then(function() use ($promise2) {
        return $promise2;
    })
    then('var_dump')
    then(function () use ($promise3) {
        return $promise3;
    })
    then('var_dump')
    then(function () {
        echo 'Complete';
    });


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



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



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



Источник: Хабр / Интересные публикации

Категория: Программирование

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

Добавление комментария

Имя:*
E-Mail:
Комментарий:
Полужирный Наклонный текст Подчеркнутый текст Зачеркнутый текст | Выравнивание по левому краю По центру Выравнивание по правому краю | Вставка смайликов Выбор цвета | Скрытый текст Вставка цитаты Преобразовать выбранный текст из транслитерации в кириллицу Вставка спойлера
Введите два слова, показанных на изображении: *