Una implementación ligera de CommonJS Promises/A para PHP.
Promise es una biblioteca que implementa CommonJS Promises/A para PHP.
También proporciona varios otros conceptos útiles relacionados con promesas, como unir múltiples promesas y mapear y reducir colecciones de promesas.
Si nunca antes has oído hablar de las promesas, lee esto primero.
Un Diferido representa un cálculo o unidad de trabajo que quizás aún no se haya completado. Normalmente (pero no siempre), ese cálculo será algo que se ejecute de forma asincrónica y se complete en algún momento en el futuro.
Mientras que un diferido representa el cálculo en sí, una Promesa representa el resultado de ese cálculo. Por lo tanto, cada aplazamiento tiene una promesa que actúa como marcador de posición para su resultado real.
Un diferido representa una operación cuya resolución está pendiente. Tiene partes separadas de promesa y resolución.
$ deferred = new React Promise Deferred ();
$ promise = $ deferred -> promise ();
$ deferred -> resolve (mixed $ value );
$ deferred -> reject (Throwable $ reason );
El método promise
devuelve la promesa del diferido.
Los métodos resolve
y reject
controlan el estado del diferido.
El constructor de Deferred
acepta un argumento $canceller
opcional. Consulte Promesa para obtener más información.
$ promise = $ deferred -> promise ();
Devuelve la promesa del diferido, que puedes entregar a otros manteniendo la autoridad para modificar su estado para ti mismo.
$ deferred -> resolve (mixed $ value );
Resuelve la promesa devuelta por promise()
. Todos los consumidores son notificados llamando $onFulfilled
(que registraron a través de $promise->then()
) con $value
.
Si $value
en sí es una promesa, la promesa pasará al estado de esta promesa una vez que se resuelva.
Vea también la función resolve()
.
$ deferred -> reject (Throwable $ reason );
Rechaza la promesa devuelta por promise()
, lo que indica que falló el cálculo del diferido. Todos los consumidores son notificados llamando $onRejected
(que registraron a través de $promise->then()
) con $reason
.
Vea también la función reject()
.
La interfaz de promesa proporciona la interfaz común para todas las implementaciones de promesas. Consulte Promise para conocer la única implementación pública expuesta por este paquete.
Una promesa representa un resultado final, que es cumplimiento (éxito) y un valor asociado, o rechazo (fracaso) y una razón asociada.
Una vez en el estado cumplido o rechazado, una promesa se vuelve inmutable. No se puede modificar ni su estado ni su resultado (o error).
$ transformedPromise = $ promise -> then (callable $ onFulfilled = null , callable $ onRejected = null );
Transforma el valor de una promesa aplicando una función al valor de cumplimiento o rechazo de la promesa. Devuelve una nueva promesa para el resultado transformado.
El método then()
registra nuevos controladores cumplidos y rechazados con una promesa (todos los parámetros son opcionales):
$onFulfilled
se invocará una vez que se cumpla la promesa y se pase el resultado como primer argumento.$onRejected
se invocará una vez que se rechace la promesa y se pase el motivo como primer argumento. Devuelve una nueva promesa que cumplirá con el valor de retorno de $onFulfilled
o $onRejected
, cualquiera que se llame, o rechazará con la excepción lanzada si cualquiera de los dos la lanza.
Una promesa ofrece las siguientes garantías sobre los controladores registrados en la misma llamada a then()
:
$onFulfilled
o $onRejected
, nunca a ambos.$onFulfilled
y $onRejected
nunca serán llamados más de una vez. $ promise -> catch (callable $ onRejected );
Registra un controlador de rechazo para la promesa. Es un atajo para:
$ promise -> then ( null , $ onRejected );
Además, puede escribir sugerencia en el argumento $reason
de $onRejected
para detectar solo errores específicos.
$ promise
-> catch ( function ( RuntimeException $ reason ) {
// Only catch RuntimeException instances
// All other types of errors will propagate automatically
})
-> catch ( function ( Throwable $ reason ) {
// Catch other errors
});
$ newPromise = $ promise -> finally (callable $ onFulfilledOrRejected );
Le permite ejecutar tareas de tipo "limpieza" en una cadena de promesa.
Organiza que se llame a $onFulfilledOrRejected
, sin argumentos, cuando la promesa se cumple o se rechaza.
$promise
se cumple y $onFulfilledOrRejected
regresa exitosamente, $newPromise
se cumplirá con el mismo valor que $promise
.$promise
se cumple y $onFulfilledOrRejected
arroja o devuelve una promesa rechazada, $newPromise
la rechazará con la excepción arrojada o el motivo de la promesa rechazada.$promise
rechaza y $onFulfilledOrRejected
regresa exitosamente, $newPromise
lo rechazará por el mismo motivo que $promise
.$promise
rechaza y $onFulfilledOrRejected
arroja o devuelve una promesa rechazada, $newPromise
rechazará con la excepción lanzada o el motivo de la promesa rechazada. finally()
se comporta de manera similar a la declaración finalmente síncrona. Cuando se combina con catch()
, finally()
le permite escribir código similar al conocido par síncrono catch/finally.
Considere el siguiente código sincrónico:
try {
return doSomething ();
} catch ( Throwable $ e ) {
return handleError ( $ e );
} finally {
cleanup ();
}
Se puede escribir código asincrónico similar (con doSomething()
que devuelve una promesa):
return doSomething ()
-> catch ( ' handleError ' )
-> finally ( ' cleanup ' );
$ promise -> cancel ();
El método cancel()
notifica al creador de la promesa que ya no hay interés en los resultados de la operación.
Una vez que se liquida una promesa (ya sea cumplida o rechazada), llamar cancel()
en una promesa no tiene ningún efecto.
En desuso desde v3.0.0, consulte
catch()
en su lugar.
El método otherwise()
registra un controlador de rechazo para una promesa.
Este método sigue existiendo sólo por motivos de BC y para facilitar la actualización entre versiones. Es un alias para:
$ promise -> catch ( $ onRejected );
En desuso desde v3.0.0, consulte
finally()
en su lugar.
El método always()
le permite ejecutar tareas de tipo "limpieza" en una cadena de promesa.
Este método sigue existiendo sólo por motivos de BC y para facilitar la actualización entre versiones. Es un alias para:
$ promise -> finally ( $ onFulfilledOrRejected );
Crea una promesa cuyo estado está controlado por las funciones pasadas a $resolver
.
$ resolver = function ( callable $ resolve , callable $ reject ) {
// Do some work, possibly asynchronously, and then
// resolve or reject.
$ resolve ( $ awesomeResult );
// or throw new Exception('Promise rejected');
// or $resolve($anotherPromise);
// or $reject($nastyError);
};
$ canceller = function () {
// Cancel/abort any running operations like network connections, streams etc.
// Reject promise by throwing an exception
throw new Exception ( ' Promise cancelled ' );
};
$ promise = new React Promise Promise ( $ resolver , $ canceller );
El constructor de la promesa recibe una función de resolución y una función de cancelación opcional que se llamarán con dos argumentos:
$resolve($value)
: función principal que sella el destino de la promesa devuelta. Acepta un valor no prometido u otra promesa. Cuando se llama con un valor no prometido, cumple la promesa con ese valor. Cuando se llama con otra promesa, por ejemplo $resolve($otherPromise)
, el destino de la promesa será equivalente al de $otherPromise
.$reject($reason)
- Función que rechaza la promesa. Se recomienda simplemente lanzar una excepción en lugar de usar $reject()
.Si el solucionador o cancelador lanza una excepción, la promesa será rechazada con esa excepción lanzada como motivo de rechazo.
La función de resolución se llamará inmediatamente, la función de cancelación solo una vez que todos los consumidores llamen al método cancel()
de la promesa.
Funciones útiles para crear y unir colecciones de promesas.
Todas las funciones que trabajan en colecciones de promesas (como all()
, race()
, etc.) admiten la cancelación. Esto significa que, si llama cancel()
en la promesa devuelta, se cancelan todas las promesas de la colección.
$ promise = React Promise resolve (mixed $ promiseOrValue );
Crea una promesa para el $promiseOrValue
proporcionado.
Si $promiseOrValue
es un valor, será el valor de resolución de la promesa devuelta.
Si $promiseOrValue
es un thenable (cualquier objeto que proporcione un método then()
), se devuelve una promesa confiable que sigue el estado del thenable.
Si $promiseOrValue
es una promesa, se devolverá tal cual.
La $promise
resultante implementa PromiseInterface
y se puede consumir como cualquier otra promesa:
$ promise = React Promise resolve ( 42 );
$ promise -> then ( function ( int $ result ): void {
var_dump ( $ result );
}, function ( Throwable $ e ): void {
echo ' Error: ' . $ e -> getMessage () . PHP_EOL ;
});
$ promise = React Promise reject (Throwable $ reason );
Crea una promesa rechazada por el $reason
proporcionado.
Tenga en cuenta que la interfaz Throwable
introducida en PHP 7 cubre los errores internos de PHP Exception
y Error
del usuario. Al aplicar Throwable
como motivo para rechazar una promesa, cualquier error de idioma o excepción del usuario se puede utilizar para rechazar una promesa.
La $promise
resultante implementa PromiseInterface
y se puede consumir como cualquier otra promesa:
$ promise = React Promise reject ( new RuntimeException ( ' Request failed ' ));
$ promise -> then ( function ( int $ result ): void {
var_dump ( $ result );
}, function ( Throwable $ e ): void {
echo ' Error: ' . $ e -> getMessage () . PHP_EOL ;
});
Tenga en cuenta que las promesas rechazadas siempre deben manejarse de manera similar a como cualquier excepción debe detectarse siempre en un bloque try
+ catch
. Si elimina la última referencia a una promesa rechazada que no ha sido manejada, informará un rechazo de promesa no manejada:
function incorrect (): int
{
$ promise = React Promise reject ( new RuntimeException ( ' Request failed ' ));
// Commented out: No rejection handler registered here.
// $promise->then(null, function (Throwable $e): void { /* ignore */ });
// Returning from a function will remove all local variable references, hence why
// this will report an unhandled promise rejection here.
return 42 ;
}
// Calling this function will log an error message plus its stack trace:
// Unhandled promise rejection with RuntimeException: Request failed in example.php:10
incorrect ();
Una promesa rechazada se considerará "manejada" si detecta el motivo del rechazo con el método then()
, el método catch()
o el método finally()
. Tenga en cuenta que cada uno de estos métodos devuelve una nueva promesa que puede volver a rechazarse si vuelve a generar una excepción.
Una promesa rechazada también se considerará "manejada" si cancela la operación con el método cancel()
(que a su vez normalmente rechazará la promesa si aún está pendiente).
Consulte también la función set_rejection_handler()
.
$ promise = React Promise all (iterable $ promisesOrValues );
Devuelve una promesa que se resolverá solo una vez que se hayan resuelto todos los elementos de $promisesOrValues
. El valor de resolución de la promesa devuelta será una matriz que contiene los valores de resolución de cada uno de los elementos en $promisesOrValues
.
$ promise = React Promise race (iterable $ promisesOrValues );
Inicia una carrera competitiva que permite un ganador. Devuelve una promesa que se resuelve de la misma manera que se resuelve la primera promesa resuelta.
La promesa devuelta quedará infinitamente pendiente si $promisesOrValues
contiene 0 elementos.
$ promise = React Promise any (iterable $ promisesOrValues );
Devuelve una promesa que se resolverá cuando se resuelva cualquiera de los elementos de $promisesOrValues
. El valor de resolución de la promesa devuelta será el valor de resolución del elemento desencadenante.
La promesa devuelta solo se rechazará si se rechazan todos los elementos de $promisesOrValues
. El valor de rechazo será ReactPromiseExceptionCompositeException
que contiene todos los motivos de rechazo. Los motivos del rechazo se pueden obtener con CompositeException::getThrowables()
.
La promesa devuelta también se rechazará con ReactPromiseExceptionLengthException
si $promisesOrValues
contiene 0 elementos.
React Promise set_rejection_handler(?callable $ callback ): ?callable;
Establece el controlador de rechazo global para rechazos de promesas no controladas.
Tenga en cuenta que las promesas rechazadas siempre deben manejarse de manera similar a como cualquier excepción debe detectarse siempre en un bloque try
+ catch
. Si elimina la última referencia a una promesa rechazada que no ha sido manejada, informará un rechazo de promesa no manejada. Consulte también la función reject()
para obtener más detalles.
El argumento ?callable $callback
DEBE ser una función de devolución de llamada válida que acepte un único argumento Throwable
o un valor null
para restaurar el controlador de rechazo de promesa predeterminado. El valor de retorno de la función de devolución de llamada se ignorará y no tendrá ningún efecto, por lo que DEBE devolver un valor void
. La función de devolución de llamada NO DEBE iniciarse o el programa finalizará con un error fatal.
La función devuelve el controlador de rechazo anterior o null
si se utiliza el controlador de rechazo de promesa predeterminado.
El controlador de rechazo de promesa predeterminado registrará un mensaje de error más su seguimiento de pila:
// Unhandled promise rejection with RuntimeException: Unhandled in example.php:2
React Promise reject ( new RuntimeException ( ' Unhandled ' ));
El controlador de rechazo de promesa se puede utilizar para personalizar el mensaje de registro o escribir en objetivos de registro personalizados. Como regla general, esta función solo debe usarse como último recurso y los rechazos de promesas se manejan mejor con el método then()
, el método catch()
o el método finally()
. Consulte también la función reject()
para obtener más detalles.
function getAwesomeResultPromise ()
{
$ deferred = new React Promise Deferred ();
// Execute a Node.js-style function using the callback pattern
computeAwesomeResultAsynchronously ( function ( Throwable $ error , $ result ) use ( $ deferred ) {
if ( $ error ) {
$ deferred -> reject ( $ error );
} else {
$ deferred -> resolve ( $ result );
}
});
// Return the promise
return $ deferred -> promise ();
}
getAwesomeResultPromise ()
-> then (
function ( $ value ) {
// Deferred resolved, do something with $value
},
function ( Throwable $ reason ) {
// Deferred rejected, do something with $reason
}
);
Algunos ejemplos simples para mostrar cómo funciona la mecánica del reenvío Promises/A. Estos ejemplos son artificiales, por supuesto, y en el uso real, las cadenas de promesas normalmente se distribuirán en varias llamadas a funciones, o incluso en varios niveles de la arquitectura de su aplicación.
Las promesas resueltas reenvían los valores de resolución a la siguiente promesa. La primera promesa, $deferred->promise()
, se resolverá con el valor pasado a $deferred->resolve()
a continuación.
Cada llamada a then()
devuelve una nueva promesa que se resolverá con el valor de retorno del controlador anterior. Esto crea una "tubería" de promesas.
$ deferred = new React Promise Deferred ();
$ deferred -> promise ()
-> then ( function ( $ x ) {
// $x will be the value passed to $deferred->resolve() below
// and returns a *new promise* for $x + 1
return $ x + 1 ;
})
-> then ( function ( $ x ) {
// $x === 2
// This handler receives the return value of the
// previous handler.
return $ x + 1 ;
})
-> then ( function ( $ x ) {
// $x === 3
// This handler receives the return value of the
// previous handler.
return $ x + 1 ;
})
-> then ( function ( $ x ) {
// $x === 4
// This handler receives the return value of the
// previous handler.
echo ' Resolve ' . $ x ;
});
$ deferred -> resolve ( 1 ); // Prints "Resolve 4"
Las promesas rechazadas se comportan de manera similar y también funcionan de manera similar para intentar/capturar: cuando detecta una excepción, debe volver a ejecutarla para que se propague.
De manera similar, cuando maneja una promesa rechazada, para propagar el rechazo, "vuelva a lanzarla" devolviendo una promesa rechazada o arrojándola (ya que la promesa traduce las excepciones lanzadas en rechazos).
$ deferred = new React Promise Deferred ();
$ deferred -> promise ()
-> then ( function ( $ x ) {
throw new Exception ( $ x + 1 );
})
-> catch ( function ( Exception $ x ) {
// Propagate the rejection
throw $ x ;
})
-> catch ( function ( Exception $ x ) {
// Can also propagate by returning another rejection
return React Promise reject (
new Exception ( $ x -> getMessage () + 1 )
);
})
-> catch ( function ( $ x ) {
echo ' Reject ' . $ x -> getMessage (); // 3
});
$ deferred -> resolve ( 1 ); // Prints "Reject 3"
Al igual que try/catch, puedes elegir propagar o no. La combinación de resoluciones y rechazos seguirá enviando los resultados del controlador de forma predecible.
$ deferred = new React Promise Deferred ();
$ deferred -> promise ()
-> then ( function ( $ x ) {
return $ x + 1 ;
})
-> then ( function ( $ x ) {
throw new Exception ( $ x + 1 );
})
-> catch ( function ( Exception $ x ) {
// Handle the rejection, and don't propagate.
// This is like catch without a rethrow
return $ x -> getMessage () + 1 ;
})
-> then ( function ( $ x ) {
echo ' Mixed ' . $ x ; // 4
});
$ deferred -> resolve ( 1 ); // Prints "Mixed 4"
La forma recomendada de instalar esta biblioteca es a través de Composer. ¿Nuevo en el compositor?
Este proyecto sigue a SemVer. Esto instalará la última versión compatible desde esta rama:
composer require react/promise:^3.2
Consulte también el CHANGELOG para obtener detalles sobre las actualizaciones de versiones.
Este proyecto pretende ejecutarse en cualquier plataforma y, por lo tanto, no requiere ninguna extensión de PHP y admite la ejecución desde PHP 7.1 hasta PHP 8+ actual. Se recomienda encarecidamente utilizar la última versión de PHP compatible para este proyecto.
Estamos comprometidos a brindar opciones de soporte a largo plazo (LTS) y a brindar una ruta de actualización sin problemas. Si está utilizando una versión anterior de PHP, puede utilizar la rama 2.x
(PHP 5.4+) o la rama 1.x
(PHP 5.3+), que proporcionan una API compatible pero no aprovechan las funciones del lenguaje más nuevo. Puede apuntar a varias versiones al mismo tiempo para admitir una gama más amplia de versiones de PHP como esta:
composer require " react/promise:^3 || ^2 || ^1 "
Para ejecutar el conjunto de pruebas, primero debe clonar este repositorio y luego instalar todas las dependencias a través de Composer:
composer install
Para ejecutar el conjunto de pruebas, vaya a la raíz del proyecto y ejecute:
vendor/bin/phpunit
Además de esto, utilizamos PHPStan en el nivel máximo para garantizar la seguridad de tipos en todo el proyecto:
vendor/bin/phpstan
Promise es una adaptación de when.js de Brian Cavalier.
Además, gran parte de la documentación se ha transferido de los documentos API y Wiki de when.js.
Publicado bajo la licencia MIT.