PHP asincrónico

Hace diez años, teníamos una pila clásica de LAMP: Linux, Apache, MySQL y PHP, que funcionaba en el modo lento de mod_php. El mundo ha cambiado, y con él la importancia de la velocidad. Apareció PHP-FPM, lo que permitió aumentar significativamente el rendimiento de las soluciones en PHP y no reescribir urgentemente algo más rápido.

Paralelamente, la biblioteca ReactPHP se desarrolló utilizando el concepto Event Loop para procesar señales del sistema operativo y presentar resultados para operaciones asincrónicas. El desarrollo de la idea de ReactPHP - AMPHP. Esta biblioteca usa el mismo Event Loop, pero admite corutinas, a diferencia de ReactPHP. Le permiten escribir código asincrónico que parece síncrono. Quizás este sea el marco más actual para desarrollar aplicaciones asincrónicas en PHP.



Pero la velocidad se requiere cada vez más, las herramientas ya no son suficientes, por lo que la idea de la programación asincrónica en PHP es una de las formas de acelerar el procesamiento de consultas y utilizar mejor los recursos.

De esto hablará Anton Shabovta (zloyusr) Es desarrollador en Onliner. Experiencia de más de 10 años: comencé con aplicaciones de escritorio en C / C ++, y luego cambié al desarrollo web en PHP. Escribe proyectos "Home" en C # y Python 3, y en PHP está experimentando con DDD, CQRS, Event Sourcing, Async Multitasking.

Este artículo se basa en una transcripción del informe de Anton sobre PHP Rusia 2019 . En él entenderemos las operaciones de bloqueo y no bloqueo en PHP, estudiaremos la estructura de Event Loop y las primitivas asíncronas, como Promise y corutinas, desde el interior. Finalmente, descubriremos lo que nos espera en ext-async, AMPHP 3 y PHP 8.


Introducimos un par de definiciones. Durante mucho tiempo intenté encontrar una definición exacta de asincronía y operaciones asincrónicas, pero no encontré y escribí la mía.
La asincronía es la capacidad de un sistema de software para no bloquear el hilo principal de ejecución.
Una operación asincrónica es una operación que no bloquea el flujo de ejecución de un programa hasta que se completa.

Parece ser simple, pero primero debe comprender qué operaciones bloquean el flujo de ejecución.

Operaciones de bloqueo


PHP es un lenguaje de intérprete. Él lee el código línea por línea, se traduce en sus instrucciones y se ejecuta. ¿En qué línea del siguiente ejemplo se bloqueará el código?

public function update(User $user)
{
    try {
        $sql = 'UPDATE users SET ...';
        return $this->connection->execute($sql, $user->data());
    } catch (\PDOException $error) {
        log($error->getMessage());
    }

    return 0;
}

Si conectamos a la base de datos a través de DOP, el hilo de ejecución será bloqueada en la cadena de consulta de SQL-servidor: return $this->connection->execute($sql, $user->data());.

Esto se debe a que PHP no sabe cuánto tiempo el servidor SQL procesará esta consulta y si se ejecutará en absoluto. Espera una respuesta del servidor y el programa no se ha estado ejecutando todo este tiempo.

PHP también bloquea el flujo de ejecución en todas las operaciones de E / S.

  • Sistema de archivos : fwrite, file_get_contents.
  • Bases de datos : PDOConnection, RedisClient. Casi todas las extensiones para conectar una base de datos funcionan en modo de bloqueo de forma predeterminada.
  • Procesos : exec, system, proc_open. Estas son operaciones de bloqueo, ya que todo el trabajo con procesos se construye a través de llamadas al sistema.
  • Trabajar con stdin / stdout : readline, echo, print.

Además, la ejecución está bloqueada en temporizadores : sleep, usleep. Estas son operaciones en las que explícitamente le decimos al hilo que se duerma por un tiempo. PHP estará inactivo todo este tiempo.

Cliente SQL asincrónico


Pero el PHP moderno es un lenguaje de propósito general, y no solo para la web como PHP / FI en 1997. Por lo tanto, podemos escribir un cliente SQL asíncrono desde cero. La tarea no es la más trivial, sino solucionable.

public function execAsync(string $query, array $params = [])
{
    $socket = stream_socket_client('127.0.0.1:3306', ...);

    stream_set_blocking($socket, false);

    $data = $this->packBinarySQL($query, $params);
    
    socket_write($socket, $data, strlen($data));
}

¿Qué hace un cliente así? Se conecta a nuestro servidor SQL, pone el socket en modo sin bloqueo, empaqueta la solicitud en un formato binario que el servidor SQL comprende, escribe datos en el socket.

Dado que el socket está en modo sin bloqueo, la operación de escritura desde PHP es rápida.

Pero, ¿qué volverá como resultado de tal operación? No sabemos qué responderá el servidor SQL. Puede tomar mucho tiempo completar la solicitud o no completarla. ¿Pero hay que devolver algo? Si usamos PDO y llamamos a la updateconsulta en el servidor SQL, se nos devuelve affected rows: la cantidad de filas cambiadas por esta consulta. Todavía no podemos devolverlo, por lo tanto, solo prometemos un retorno.

Promesa


Este es un concepto del mundo de la programación asincrónica.
Promise es un objeto contenedor sobre el resultado de una operación asincrónica. Además, el resultado de la operación aún es desconocido para nosotros.
Desafortunadamente, no existe un único estándar de Promise, y no es posible transferir directamente estándares del mundo de JavaScript a PHP.

Cómo funciona la promesa


Como todavía no hay resultados, solo podemos establecer algunos callbacks.



Cuando hay datos disponibles, es necesario ejecutar una devolución de llamada onResolve.



Si se produce un error, se ejecutará una devolución de llamada onRejectpara manejar el error.



La interfaz de Promise se parece a esto.

interface Promise
{
    const
        STATUS_PENDING = 0,
        STATUS_RESOLVED = 1,
        STATUS_REJECTED = 2
    ;

    public function onResolve(callable $callback);
    public function onReject(callable $callback);
    public function resolve($data);
    public function reject(\Throwable $error);
}

Promise tiene estado y métodos para establecer devoluciones de llamada y completar ( resolve) Promesa con datos o error ( reject). Pero hay diferencias y variaciones. Los métodos pueden llamarse de manera diferente, o en lugar de métodos separados para establecer devoluciones de llamada, resolvey rejectpuede haber alguno, como en AMPHP, por ejemplo.

A menudo, las técnicas para llenar Promise resolvey rejectsacar en un objeto separado la función asincrónica de estado de almacenamiento diferido . Se puede considerar como una especie de fábrica para Promise. Es de una sola vez: un diferido hace una promesa.



¿Cómo aplicar esto en el cliente SQL si decidimos escribirlo nosotros mismos?

Cliente SQL asincrónico


Primero, creamos diferido, hicimos todo el trabajo con sockets, escribimos los datos y devolvimos Promise: todo es simple.

public function execAsync(string $query, array $params = [])
{
    $deferred = new Deferred;

    $socket = stream_socket_client('127.0.0.1:3306', ...);
    stream_set_blocking($socket, false);

    $data = $this->packBinarySQL($query, $params);
    socket_write($socket, $data, strlen($data));

    return $deferred->promise();
}

Cuando tenemos Promesa, podemos, por ejemplo:

  • configure la devolución de llamada y obtenga los affected rowsque nos devuelven PDOConnection;
  • manejar el error, agregar al registro;
  • Vuelva a intentar la consulta si el servidor SQL responde con un error.

$promise = $this->execAsync($sql, $user->data());

$promise->onResolve(function (int $rows) {
    echo "Affected rows: {$rows}";
});

$promise->onReject(function (\Throwable $error) {
    log($error->getMessage());
});

La pregunta sigue siendo: establecemos la devolución de llamada, ¿y quién llamará resolvey reject?

Bucle de eventos


Existe el concepto de bucle de eventos, un bucle de eventos . Es capaz de procesar mensajes en un entorno asíncrono. Para E / S asincrónicas, estos serán mensajes del sistema operativo que el socket está listo para leer o escribir.

Cómo funciona.

  • El cliente le dice a Event Loop que está interesado en algún tipo de socket.
  • Event Loop sondea el sistema operativo a través de una llamada al sistema stream_select: está listo el socket, están escritos todos los datos, son los datos que provienen del otro lado.
  • Si el sistema operativo informa que el socket no está listo, bloqueado, entonces Event Loop repite el ciclo.
  • Cuando el sistema operativo notifica que el socket está listo, Event Loop devuelve el control al cliente y habilita ( resolveo reject) Promise.



Expresamos este concepto en el código: tome el caso más simple, elimine el manejo de errores y otros matices, para que quede un bucle infinito. En cada iteración, sondeará el sistema operativo sobre los sockets que están listos para leer o escribir, y llamará a una devolución de llamada para un socket específico.

public static function run()
{
    while (true) {
        stream_select($readSockets, $writeSockets, null, 0);
        
        foreach ($readSockets as $i => $socket) {
            call_user_func(self::readCallbacks[$i], $socket);
        }

        // Do same for write sockets
    }
}

Complementamos nuestro cliente SQL. Informamos a Event Loop que tan pronto como los datos del servidor SQL lleguen al socket con el que estamos trabajando, debemos llevar el estado diferido al estado "hecho" y transferir los datos del socket a Promise.

public function execAsync(string $query, array $params = [])
{
    $deferred = new Deferred;
    ...
    Loop::onReadable($socket, function ($socket) use ($deferred) {
        $deferred->resolve(socket_read($socket));
    });

    return $deferred->promise();
}

Event Loop puede manejar nuestras E / S y funciona con sockets . ¿Qué más puede hacer él?

  • JavaScript setTimeout setInterval — . N . Event Loop .
  • Event Loop . process control, .

Event Loop


Escribir su Event Loop no solo es posible, sino también necesario. Si desea trabajar con PHP asincrónico, es importante escribir su propia implementación simple para comprender cómo funciona esto. Pero en producción, por supuesto, no lo usaremos, pero tomaremos implementaciones listas para usar: estables, sin errores y probadas en el trabajo.

Hay tres implementaciones principales.

ReactPHP . El proyecto más antiguo, comenzó en PHP 5.3. Ahora la versión mínima requerida de PHP es 5.3.8. El proyecto implementa el estándar Promises / A del mundo de JavaScript.

AMPHP . Es esta implementación la que prefiero usar. El requisito mínimo es PHP 7.0, y dado que la próxima versión ya es 7.3. Utiliza corutinas encima de Promise.

Swoole. Este es un marco chino interesante en el que los desarrolladores intentan transferir algunos conceptos del mundo Go a PHP. La documentación en inglés está incompleta, la mayoría en GitHub en chino. Si sabes el idioma, adelante, pero por ahora tengo miedo de trabajar.



ReactPHP


Veamos cómo se verá el cliente usando ReactPHP para MySQL.

$connection = (new ConnectionFactory)->createLazyConnection();

$promise = $connection->query('UPDATE users SET ...');
$promise->then(
    function (QueryResult $command) {
        echo count($command->resultRows) . ' row(s) in set.';
    },
    function (Exception $error) {
        echo 'Error: ' . $error->getMessage();
    });

Todo es casi lo mismo que escribimos: creamos onnectiony ejecutamos la solicitud. Podemos configurar la devolución de llamada para procesar los resultados (retorno affected rows):

    function (QueryResult $command) {
        echo count($command->resultRows) . ' row(s) in set.';
    },

y devolución de llamada para el manejo de errores:

    function (Exception $error) {
        echo 'Error: ' . $error->getMessage();
    });

A partir de estas devoluciones de llamada, puede construir cadenas largas y largas, porque cada resultado thenen ReactPHP también devuelve Promise.

$promise
    ->then(function ($data) {
        return new Promise(...);
    })
    ->then(function ($data) {
        ...
    }, function ($error) {
        log($error);
    })
    ...

Esta es una solución a un problema llamado callback hell. Desafortunadamente, en la implementación de ReactPHP, esto lleva al problema de "Promesa del infierno", cuando se requieren 10-11 devoluciones de llamada para conectar RabbitMQ correctamente . Trabajar con dicho código y solucionarlo es difícil. Rápidamente me di cuenta de que esto no era mío y cambié a AMPHP.

Amphp


Este proyecto es más joven que ReactPHP y promueve un concepto diferente: las corutinas . Si observa trabajar con MySQL en AMPHP, puede ver que esto es casi lo mismo que trabajar con PDOConnectionPHP.

$pool = Mysql\pool("host=127.0.0.1 port=3306 db=test");

try {
    $result = yield $pool->query("UPDATE users SET ...");

    echo $result->affectedRows . ' row(s) in set.';
} catch (\Throwable $error) {
    echo 'Error: ' . $error->getMessage();
}

Aquí creamos un grupo, conectamos y ejecutamos la solicitud. Podemos manejar los errores a través de los habituales try...catch, no necesitamos devoluciones de llamada.

Pero antes de la llamada asincrónica, la palabra clave - aparece aquí yield.

Generadores


La palabra clave yieldconvierte nuestra función en un generador.

function generator($counter = 1)
{
    yield $counter++;

    echo "A";

    yield $counter;

    echo "B";

    yield ++$counter;
}

Tan pronto como el intérprete PHP encuentra yieldfunciones en el cuerpo, se da cuenta de que es una función generadora. En lugar de ejecutar, se crea un objeto de clase cuando se llama Generator.

Los generadores heredan la interfaz del iterador.

$generator = generator(1);

foreach ($generator as $value) {
    echo $value;
}

while ($generator->valid()) {
    echo $generator->current();

    $generator->next();
}

En consecuencia, es posible ejecutar ciclos foreachy whileentre otros. Pero, más interesante, el iterador tiene métodos currenty next. Vamos a revisarlos paso a paso.

Ejecuta nuestra función generator($counter = 1). Llamamos al método generador current(). Se devolverá el valor de la variable $counter++.

Tan pronto como ejecutemos el generador next(), el código irá a la siguiente llamada dentro del generador yield. Se yieldejecutará todo el código entre los dos , y eso es genial. Continuando girando el generador, obtenemos el resultado.

Corutinas


Pero el generador tiene una función más interesante: podemos enviar datos al generador desde el exterior. En este caso, esto no es un generador, sino una corutina o corutina.

function printer() {  
    while (true) {     
        echo yield;       
    }                             
}                                

$print = printer();
$print->send('Hello');
$print->send(' PHPRussia');
$print->send(' 2019');
$print->send('!');

En esta sección del código, es interesante que while (true)no bloqueará el flujo de ejecución, sino que se ejecutará una vez. Enviamos los datos a Corutin y los recibimos 'Hello'. Enviado más - recibido 'PHPRussia'. El principio es claro.

Además de enviar datos al generador, puede enviar errores y procesarlos desde el interior, lo cual es conveniente.

function printer() {
    try {
        echo yield;
    } catch (\Throwable $e) {
        echo $e->getMessage();
    }
}

printer()->throw(new \Exception('Ooops...'));

Para resumir. Corutin es un componente de un programa que permite detener y continuar la ejecución mientras se mantiene el estado actual . Corutin recuerda su pila de llamadas, los datos que contiene y puede usarlos en el futuro.

Generadores y promesa


Veamos el generador y las interfaces de Promise.

class Generator
{
    public function send($data);
    public function throw(\Throwable $error);
}

class Promise
{
    public function resolve($data);
    public function reject(\Throwable $error);
}

Se ven iguales, excepto por diferentes nombres de métodos. Podemos enviar datos y lanzar un error tanto al generador como a Promise.

¿Cómo se puede usar esto? Escribamos una función.

function recoil(\Generator $generator)
{
    $promise = $generator->current();

    $promise->onResolve(function($data) use ($generator) {
        $generator->send($data);
        recoil($generator);
    };

    $promise->onReject(function ($error) use ($generator) {
        $generator->throw($error);
        recoil($generator);
    });
}

La función toma el valor de la corriente del generador: $promise = $generator->current();.

Exageré un poco. Sí, debemos verificar que el valor actual que se nos devuelve es algún tipo de instanceofPromesa. Si es así, podemos pedirle una devolución de llamada. Internamente envía los datos de regreso al generador cuando Promise tiene éxito y recursivamente inicia la función recoil.

    $promise->onResolve(function($data) use ($generator) {
        $generator->send($data);
        recoil($generator);
    };

Lo mismo se puede hacer con errores. Si Promise falló, por ejemplo, el servidor SQL dijo: "Demasiadas conexiones", entonces podemos arrojar el error dentro del generador e ir al siguiente paso.

Todo esto nos lleva al importante concepto de multitarea cooperativa.

Multitarea cooperativa


Este es un tipo de multitarea, en el que la siguiente tarea se realiza solo después de que la tarea actual se declara explícitamente lista para darle tiempo al procesador para otras tareas.

Raramente encuentro algo simple, como trabajar con una sola base de datos. Muy a menudo, en el proceso de actualización del usuario, debe actualizar los datos en la base de datos, en el índice de búsqueda, luego limpiar o actualizar el caché y luego enviar 15 mensajes más a RabbitMQ. En PHP, todo se ve así.



Realizamos operaciones una por una: actualizamos la base de datos, el índice y luego el caché. Pero, de manera predeterminada, PHP bloquea estas operaciones (E / S), por lo que si observa de cerca, de hecho, todo es así.



En las partes oscuras bloqueamos. Toman la mayor parte del tiempo.

Si trabajamos en modo asíncrono, entonces estas partes no están allí, la línea de tiempo de ejecución es intermitente.



Puedes pegarlo todo junto y hacer piezas una por una.



¿Para qué es todo esto? Si observa el tamaño de la línea de tiempo, al principio lleva mucho tiempo, pero tan pronto como lo pegamos, la aplicación se acelera.

El concepto mismo de Event Loop y la multitarea cooperativa se ha utilizado durante mucho tiempo en diversas aplicaciones: Nginx, Node.js, Memcached, Redis. Todos ellos se usan dentro de Event Loop y se basan en el mismo principio.

Desde que comenzamos a hablar sobre los servidores web Nginx y Node.js, recordemos cómo se lleva a cabo el procesamiento de solicitudes en PHP.

Procesamiento de solicitudes en PHP


El navegador envía una solicitud, llega al servidor HTTP detrás del cual hay un grupo de transmisiones FPM. Uno de los subprocesos pone en funcionamiento esta solicitud, conecta nuestro código y comienza a ejecutarlo.



Cuando llegue la próxima solicitud, otro subproceso de FPM lo recogerá, conectará el código y se ejecutará.

Hay ventajas en este esquema de trabajo .

  • Manejo simple de errores . Si algo salió mal y una de las solicitudes cayó, no necesitamos hacer nada; la próxima vendrá, y esto no afectará su trabajo.
  • No pensamos en la memoria . No necesitamos limpiar ni monitorear la memoria. En la siguiente solicitud, se borrará toda la memoria.

Este es un esquema genial que funcionó en PHP desde el principio y aún funciona con éxito. Pero también hay desventajas .

  • Limite el número de procesos . Si tenemos 50 subprocesos FPM en el servidor, tan pronto como llegue la solicitud 51, esperará hasta que uno de los subprocesos se libere.
  • Costos por cambio de contexto . El sistema operativo cambia las solicitudes entre transmisiones FPM. Esta operación a nivel de procesador se llama Cambio de contexto. Es costoso y ejecuta una gran cantidad de medidas. Es necesario guardar todos los registros, la pila de llamadas, todo lo que está en el procesador, luego cambiar a otro proceso, cargar sus registros y su pila de llamadas, realizar algo allí nuevamente, cambiar de nuevo, guardar de nuevo ... Durante mucho tiempo.

Abordemos la pregunta de manera diferente: escribiremos un servidor HTTP en el propio PHP.

Servidor HTTP asincrónico




Se puede hacer. Ya hemos aprendido cómo trabajar con sockets en modo sin bloqueo, y una conexión HTTP es el mismo socket. ¿Cómo se verá y funcionará?

Este es un ejemplo de inicio de servidores HTTP en el marco AMPHP.

Loop::run(function () {
    $app = new Application();
    $app->bootstrap();

    $sockets = [Socket\listen('0.0.0.0:80')];

    $server = new Server($sockets, new CallableRequestHandler(
        function (Request $request) use ($app) {
            $response = yield $app->dispatch($request);

            return new Response(Status::OK, [], $response);
        })
    );

    yield $server->start();
});

Todo es bastante simple: cargar Applicationy crear un grupo de sockets (uno o más).

A continuación, iniciamos nuestro servidor, lo configuramos Handler, que se ejecutará en cada solicitud y enviaremos la solicitud a la nuestra Applicationpara obtener una respuesta.

Lo último que debe hacer es iniciar el servidor yield $server->start();.

En ReactPHP se verá aproximadamente igual, pero solo habrá 150 devoluciones de llamada para diferentes opciones, lo que no es muy conveniente.

Problemas


Hay varios problemas con la asincronía en PHP.

Falta de estándares . Cada marco: Swoole, ReactPHP o AMPHP, implementa su propia interfaz Promise, y son incompatibles.

AMPHP podría interactuar teóricamente con Promise de ReactPHP, pero hay una advertencia. Si el código de ReactPHP no está muy bien escrito, y en algún lugar llama o crea implícitamente un bucle de eventos, resulta que dos bucles de eventos girarán dentro.

JavaScript tiene un estándar relativamente bueno Promesas / A + que implementa Guzzle. Sería bueno si los marcos lo siguen. Pero hasta ahora esto no es así.

Pérdidas de memoria. Cuando trabajamos en PHP en el modo FPM habitual, es posible que no pensemos en la memoria. Incluso si los desarrolladores de alguna extensión olvidaron escribir un buen código, olvidaron ejecutar Valgrind y en algún lugar dentro de los flujos de memoria, entonces está bien: la próxima solicitud se borrará y comenzará nuevamente. Pero en modo asíncrono, no puede permitirse esto, porque tarde o temprano simplemente nos caeremos OutOfMemoryException.

Es posible repararlo, pero es difícil y doloroso. En algunos casos, Xdebug ayuda, en otros, a analizar los errores que causaron OutOfMemoryException.

Operaciones de bloqueo . Es vital no bloquear Event Loop cuando escribimos código asincrónico. La aplicación se ralentiza tan pronto como bloqueamos el flujo de ejecución, cada una de nuestras rutinas comienza a correr más lentamente.

El paquete kelunik / loop-block ayudará a encontrar tales operaciones para AMPHP . Establece el temporizador en un intervalo muy pequeño. Si el temporizador no funciona, entonces estamos bloqueados en alguna parte. El paquete ayuda a encontrar lugares de bloqueo, pero no siempre: puede que no se note el bloqueo en algunas extensiones.

Soporte de biblioteca: Cassandra, Influx, ClickHouse . El principal problema de todo PHP asincrónico es el soporte de bibliotecas. No podemos utilizar los habituales PDOConnection, RedisClientotros controladores para todos - necesitamos implementaciones no bloqueantes. También deben escribirse en PHP en modo sin bloqueo, porque los controladores C rara vez proporcionan interfaces que se puedan integrar en código asíncrono.

La experiencia más extraña que tuve con el controlador de la base de datos Cassandra. Proporcionan operacionesExecuteAsync, GetAsyncy otros, pero al mismo tiempo devuelven un objeto Futurecon un único método getque bloquea. Existe la oportunidad de obtener algo de forma asincrónica, pero para esperar el resultado, seguiremos bloqueando todo nuestro Loop. Para hacerlo de alguna manera diferente, por ejemplo, a través de devoluciones de llamada, no funciona. Incluso escribí mi cliente para Cassandra, porque lo usamos en nuestro trabajo.

Indicación de tipo . Este es un problema de AMPHP y corutina.

class UserRepository
{
    public function find(int $id): \Generator
    {
        $data = yield $this->db->query('SELECT ...', $id);

        return User::fill($data);
    }
}

Si ocurre en una función yield, se convierte en un generador. En este punto, ya no podemos especificar los tipos de datos de retorno correctos.

PHP 8


¿Qué nos espera en PHP 8? Te contaré sobre mis suposiciones o, más bien, mis deseos ( nota del editor: Dmitry Stogov sabe lo que realmente aparecerá en PHP 8 ).

Bucle de eventos Existe la posibilidad de que aparezca, porque se está trabajando para llevar Event Loop de alguna forma al núcleo. Si esto sucede, tendremos una función await, como en JavaScript o C #, que nos permitirá esperar el resultado de la operación asincrónica en un lugar determinado. En este caso, no necesitaremos ninguna extensión, todo funcionará de forma asíncrona a nivel del núcleo.


class UserRepository
{
    public function find(int $id): Promise<User>
    {
        $data = await $this->db->query('SELECT ...', $id);

        return User::fill($data);
    }
}


Genéricos Go está esperando Genéricos, estamos esperando Genéricos, todos están esperando Genéricos.

class UserRepository
{
    public function find(int $id): Promise<User>
    {
        $data = yield $this->db->query('SELECT ...', $id);

        return User::fill($data);
    }
}

Pero no estamos esperando colecciones genéricas, sino indicar que el resultado de Promise será exactamente el objeto Usuario.

¿Por qué todo esto?

Por velocidad y rendimiento.
PHP es un lenguaje en el que la mayoría de las operaciones están vinculadas a E / S. Raramente escribimos código que esté significativamente vinculado a los cálculos en el procesador. Lo más probable es que trabajemos con sockets: necesitamos hacer una solicitud a la base de datos, leer algo, devolver una respuesta, enviar un archivo. Asynchrony le permite acelerar dicho código. Si observamos el tiempo de respuesta promedio para 1000 solicitudes, podemos acelerar unas 8 veces, ¡y 10,000 solicitudes en casi 6!

El 13 de mayo de 2020, nos reuniremos por segunda vez en PHP Rusia para discutir el lenguaje, las bibliotecas y los marcos, las formas de aumentar la productividad y las dificultades de las soluciones publicitarias. Hemos aceptado los primeros 4 informes , pero Call for Papers aún está por llegar. Solicite si desea compartir su experiencia con la comunidad.

Source: https://habr.com/ru/post/undefined/


All Articles