Une implémentation légère de CommonJS Promises/A pour PHP.
Promise est une bibliothèque implémentant CommonJS Promises/A pour PHP.
Il fournit également plusieurs autres concepts utiles liés aux promesses, tels que la jonction de plusieurs promesses et le mappage et la réduction de collections de promesses.
Si vous n’avez jamais entendu parler de promesses auparavant, lisez ceci en premier.
Un différé représente un calcul ou une unité de travail qui n’est peut-être pas encore terminé. Généralement (mais pas toujours), ce calcul sera quelque chose qui s'exécutera de manière asynchrone et se terminera à un moment donné dans le futur.
Alors qu'un différé représente le calcul lui-même, une promesse représente le résultat de ce calcul. Ainsi, chaque différé a une promesse qui sert d’espace réservé à son résultat réel.
Un différé représente une opération dont la résolution est en attente. Il comporte des parties de promesse et de résolution distinctes.
$ deferred = new React Promise Deferred ();
$ promise = $ deferred -> promise ();
$ deferred -> resolve (mixed $ value );
$ deferred -> reject (Throwable $ reason );
La méthode promise
renvoie la promesse du différé.
Les méthodes resolve
et reject
contrôlent l’état du différé.
Le constructeur du Deferred
accepte un argument $canceller
facultatif. Voir Promesse pour plus d’informations.
$ promise = $ deferred -> promise ();
Renvoie la promesse du différé, que vous pouvez distribuer aux autres tout en gardant le pouvoir de modifier son état pour vous-même.
$ deferred -> resolve (mixed $ value );
Résout la promesse renvoyée par promise()
. Tous les consommateurs sont informés en faisant appeler $onFulfilled
(qu'ils ont enregistré via $promise->then()
) avec $value
.
Si $value
elle-même est une promesse, la promesse passera à l'état de cette promesse une fois résolue.
Voir aussi la fonction resolve()
.
$ deferred -> reject (Throwable $ reason );
Rejette la promesse renvoyée par promise()
, signalant que le calcul du différé a échoué. Tous les consommateurs sont avertis en faisant appeler $onRejected
(qu'ils ont enregistré via $promise->then()
) avec $reason
.
Voir aussi la fonction reject()
.
L'interface de promesse fournit l'interface commune pour toutes les implémentations de promesse. Voir Promise pour la seule implémentation publique exposée par ce package.
Une promesse représente un résultat éventuel, qui est soit un accomplissement (succès) et une valeur associée, soit un rejet (échec) et une raison associée.
Une fois à l’état réalisé ou rejeté, une promesse devient immuable. Ni son état ni son résultat (ou erreur) ne peuvent être modifiés.
$ transformedPromise = $ promise -> then (callable $ onFulfilled = null , callable $ onRejected = null );
Transforme la valeur d'une promesse en appliquant une fonction à la valeur de réalisation ou de rejet de la promesse. Renvoie une nouvelle promesse pour le résultat transformé.
La méthode then()
enregistre les nouveaux gestionnaires d'exécution et de rejet avec une promesse (tous les paramètres sont facultatifs) :
$onFulfilled
sera invoqué une fois la promesse remplie et transmettra le résultat comme premier argument.$onRejected
sera invoqué une fois la promesse rejetée et la raison transmise comme premier argument. Il renvoie une nouvelle promesse qui se réalisera avec la valeur de retour $onFulfilled
ou $onRejected
, selon celui qui est appelé, ou qui la rejettera avec l'exception levée si l'un ou l'autre est lancé.
Une promesse offre les garanties suivantes sur les gestionnaires enregistrés dans le même appel à then()
:
$onFulfilled
ou $onRejected
sera appelé, jamais les deux.$onFulfilled
et $onRejected
ne seront jamais appelés plus d'une fois. $ promise -> catch (callable $ onRejected );
Enregistre un gestionnaire de rejet pour promesse. C'est un raccourci pour :
$ promise -> then ( null , $ onRejected );
De plus, vous pouvez taper l'argument $reason
de $onRejected
pour détecter uniquement des erreurs spécifiques.
$ 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 );
Permet d'exécuter des tâches de type "cleanup" dans une chaîne de promesse.
Il fait en sorte que $onFulfilledOrRejected
soit appelé, sans argument, lorsque la promesse est remplie ou rejetée.
$promise
se réalise et que $onFulfilledOrRejected
revient avec succès, $newPromise
se remplira avec la même valeur que $promise
.$promise
se réalise et que $onFulfilledOrRejected
renvoie ou renvoie une promesse rejetée, $newPromise
la rejettera avec l'exception levée ou la raison de la promesse rejetée.$promise
est rejeté et que $onFulfilledOrRejected
est renvoyé avec succès, $newPromise
sera rejeté pour la même raison que $promise
.$promise
rejette et que $onFulfilledOrRejected
lance ou renvoie une promesse rejetée, $newPromise
rejettera avec l'exception levée ou la raison de la promesse rejetée. finally()
se comporte de la même manière que l'instruction synchrone enfin. Lorsqu'il est combiné avec catch()
, finally()
vous permet d'écrire du code similaire à la paire synchrone habituelle catch/finally.
Considérez le code synchrone suivant :
try {
return doSomething ();
} catch ( Throwable $ e ) {
return handleError ( $ e );
} finally {
cleanup ();
}
Un code asynchrone similaire (avec doSomething()
qui renvoie une promesse) peut être écrit :
return doSomething ()
-> catch ( ' handleError ' )
-> finally ( ' cleanup ' );
$ promise -> cancel ();
La méthode cancel()
informe le créateur de la promesse qu'il n'y a plus d'intérêt pour les résultats de l'opération.
Une fois qu'une promesse est réglée (soit remplie, soit rejetée), appeler cancel()
sur une promesse n'a aucun effet.
Obsolète depuis la version 3.0.0, voir
catch()
à la place.
La méthode otherwise()
enregistre un gestionnaire de rejet pour une promesse.
Cette méthode continue d'exister uniquement pour des raisons de Colombie-Britannique et pour faciliter la mise à niveau entre les versions. C'est un alias pour :
$ promise -> catch ( $ onRejected );
Obsolète depuis la v3.0.0, voir
finally()
à la place.
La méthode always()
permet d'exécuter des tâches de type "cleanup" dans une chaîne de promesses.
Cette méthode continue d'exister uniquement pour des raisons de Colombie-Britannique et pour faciliter la mise à niveau entre les versions. C'est un alias pour :
$ promise -> finally ( $ onFulfilledOrRejected );
Crée une promesse dont l'état est contrôlé par les fonctions passées à $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 );
Le constructeur de promesse reçoit une fonction de résolution et une fonction d'annulation facultative qui seront toutes deux appelées avec deux arguments :
$resolve($value)
- Fonction principale qui scelle le sort de la promesse renvoyée. Accepte soit une valeur non promise, soit une autre promesse. Lorsqu'il est appelé avec une valeur non promise, remplit la promesse avec cette valeur. Lorsqu'elle est appelée avec une autre promesse, par exemple $resolve($otherPromise)
, le sort de la promesse sera équivalent à celui de $otherPromise
.$reject($reason)
- Fonction qui rejette la promesse. Il est recommandé de simplement lever une exception au lieu d'utiliser $reject()
.Si le résolveur ou l'annuleur lève une exception, la promesse sera rejetée avec cette exception levée comme motif de rejet.
La fonction résolveur sera appelée immédiatement, la fonction annuleur seulement une fois que tous les consommateurs auront appelé la méthode cancel()
de la promesse.
Fonctions utiles pour créer et rejoindre des collections de promesses.
Toutes les fonctions travaillant sur les collections de promesses (comme all()
, race()
, etc.) prennent en charge l'annulation. Cela signifie que si vous appelez cancel()
sur la promesse renvoyée, toutes les promesses de la collection sont annulées.
$ promise = React Promise resolve (mixed $ promiseOrValue );
Crée une promesse pour le $promiseOrValue
fourni.
Si $promiseOrValue
est une valeur, ce sera la valeur de résolution de la promesse renvoyée.
Si $promiseOrValue
est un thenable (tout objet qui fournit une méthode then()
), une promesse fiable qui suit l'état du thenable est renvoyée.
Si $promiseOrValue
est une promesse, elle sera renvoyée telle quelle.
La $promise
résultante implémente PromiseInterface
et peut être consommée comme n'importe quelle autre promesse :
$ 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 );
Crée une promesse rejetée pour le $reason
fourni.
Notez que l'interface Throwable
introduite dans PHP 7 couvre à la fois les erreurs PHP internes des utilisateurs Exception
et Error
. En appliquant Throwable
comme raison pour rejeter une promesse, toute erreur de langue ou exception utilisateur peut être utilisée pour rejeter une promesse.
La $promise
résultante implémente PromiseInterface
et peut être consommée comme n'importe quelle autre promesse :
$ 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 ;
});
Notez que les promesses rejetées doivent toujours être traitées de la même manière que les exceptions doivent toujours être interceptées dans un bloc try
+ catch
. Si vous supprimez la dernière référence à une promesse rejetée qui n'a pas été traitée, un rejet de promesse non géré sera signalé :
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 ();
Une promesse rejetée sera considérée comme « gérée » si vous détectez la raison du rejet avec la méthode then()
, la méthode catch()
ou la méthode finally()
. Notez que chacune de ces méthodes renvoie une nouvelle promesse qui peut à nouveau être rejetée si vous relancez une exception.
Une promesse rejetée sera également considérée comme « gérée » si vous abandonnez l'opération avec la méthode cancel()
(qui à son tour rejetterait généralement la promesse si elle est toujours en attente).
Voir aussi la fonction set_rejection_handler()
.
$ promise = React Promise all (iterable $ promisesOrValues );
Renvoie une promesse qui ne sera résolue qu'une fois que tous les éléments de $promisesOrValues
auront été résolus. La valeur de résolution de la promesse renvoyée sera un tableau contenant les valeurs de résolution de chacun des éléments de $promisesOrValues
.
$ promise = React Promise race (iterable $ promisesOrValues );
Lance une course compétitive qui permet à un gagnant. Renvoie une promesse qui est résolue de la même manière que la première promesse réglée.
La promesse renvoyée deviendra infiniment en attente si $promisesOrValues
contient 0 élément.
$ promise = React Promise any (iterable $ promisesOrValues );
Renvoie une promesse qui sera résolue lorsque l'un des éléments de $promisesOrValues
sera résolu. La valeur de résolution de la promesse renvoyée sera la valeur de résolution de l’élément déclencheur.
La promesse renvoyée ne sera rejetée que si tous les éléments de $promisesOrValues
sont rejetés. La valeur de rejet sera une ReactPromiseExceptionCompositeException
qui contient toutes les raisons de rejet. Les raisons du rejet peuvent être obtenues avec CompositeException::getThrowables()
.
La promesse renvoyée sera également rejetée avec une ReactPromiseExceptionLengthException
si $promisesOrValues
contient 0 élément.
React Promise set_rejection_handler(?callable $ callback ): ?callable;
Définit le gestionnaire de rejet global pour les rejets de promesses non gérés.
Notez que les promesses rejetées doivent toujours être traitées de la même manière que les exceptions doivent toujours être interceptées dans un bloc try
+ catch
. Si vous supprimez la dernière référence à une promesse rejetée qui n'a pas été traitée, un rejet de promesse non géré sera signalé. Voir aussi la fonction reject()
pour plus de détails.
L'argument ?callable $callback
DOIT être une fonction de rappel valide qui accepte un seul argument Throwable
ou une valeur null
pour restaurer le gestionnaire de rejet de promesse par défaut. La valeur de retour de la fonction de rappel sera ignorée et n'aura aucun effet, vous DEVRIEZ donc renvoyer une valeur void
. La fonction de rappel NE DOIT PAS être lancée, sinon le programme se terminera avec une erreur fatale.
La fonction renvoie le gestionnaire de rejet précédent ou null
si vous utilisez le gestionnaire de rejet de promesse par défaut.
Le gestionnaire de rejet de promesse par défaut enregistrera un message d'erreur ainsi que sa trace de pile :
// Unhandled promise rejection with RuntimeException: Unhandled in example.php:2
React Promise reject ( new RuntimeException ( ' Unhandled ' ));
Le gestionnaire de rejet de promesse peut être utilisé pour personnaliser le message du journal ou écrire sur des cibles de journal personnalisées. En règle générale, cette fonction ne doit être utilisée qu'en dernier recours et les rejets de promesses sont mieux gérés avec la méthode then()
, la méthode catch()
ou la méthode finally()
. Voir aussi la fonction reject()
pour plus de détails.
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
}
);
Quelques exemples simples pour montrer comment fonctionne la mécanique du transfert Promises/A. Ces exemples sont bien sûr artificiels et, dans la réalité, les chaînes de promesses seront généralement réparties sur plusieurs appels de fonction, voire sur plusieurs niveaux de l'architecture de votre application.
Les promesses résolues transmettent les valeurs de résolution à la promesse suivante. La première promesse, $deferred->promise()
, sera résolue avec la valeur transmise à $deferred->resolve()
ci-dessous.
Chaque appel à then()
renvoie une nouvelle promesse qui sera résolue avec la valeur de retour du gestionnaire précédent. Cela crée un « pipeline » de promesses.
$ 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"
Les promesses rejetées se comportent de la même manière et fonctionnent également de la même manière pour essayer/attraper : lorsque vous interceptez une exception, vous devez la relancer pour qu'elle se propage.
De même, lorsque vous gérez une promesse rejetée, pour propager le rejet, "relancez-la" soit en renvoyant une promesse rejetée, soit en la lançant (puisque la promesse traduit les exceptions levées en rejets)
$ 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"
Tout comme try/catch, vous pouvez choisir de vous propager ou non. Le mélange des résolutions et des rejets transmettra toujours les résultats du gestionnaire de manière prévisible.
$ 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 méthode recommandée pour installer cette bibliothèque consiste à utiliser Composer. Nouveau sur Composer ?
Ce projet fait suite à SemVer. Cela installera la dernière version prise en charge à partir de cette branche :
composer require react/promise:^3.2
Voir également le CHANGELOG pour plus de détails sur les mises à niveau de version.
Ce projet vise à fonctionner sur n'importe quelle plate-forme et ne nécessite donc aucune extension PHP et prend en charge l'exécution sur PHP 7.1 jusqu'à PHP 8+ actuel. Il est fortement recommandé d'utiliser la dernière version de PHP prise en charge pour ce projet.
Nous nous engageons à fournir des options de support à long terme (LTS) et à fournir un chemin de mise à niveau fluide. Si vous utilisez une ancienne version de PHP, vous pouvez utiliser la branche 2.x
(PHP 5.4+) ou la branche 1.x
(PHP 5.3+) qui fournissent toutes deux une API compatible mais ne profitent pas des fonctionnalités du langage les plus récentes. Vous pouvez cibler plusieurs versions en même temps pour prendre en charge un plus large éventail de versions de PHP, comme ceci :
composer require " react/promise:^3 || ^2 || ^1 "
Pour exécuter la suite de tests, vous devez d'abord cloner ce dépôt, puis installer toutes les dépendances via Composer :
composer install
Pour exécuter la suite de tests, accédez à la racine du projet et exécutez :
vendor/bin/phpunit
De plus, nous utilisons PHPStan au niveau maximum pour garantir la sécurité des types tout au long du projet :
vendor/bin/phpstan
Promise est un portage de when.js par Brian Cavalier.
En outre, une grande partie de la documentation a été portée à partir du wiki when.js et de la documentation de l'API.
Publié sous licence MIT.