WebSockets
Vous pouvez trouver tout le code de ce chapitre ici
Dans ce chapitre, nous allons apprendre à utiliser les WebSockets pour améliorer notre application.
Rappel du projet
Nous avons deux applications dans notre code poker :
Application en ligne de commande. Elle demande à l'utilisateur d'entrer le nombre de joueurs dans une partie. Ensuite, elle informe les joueurs de la valeur de la "mise obligatoire" (blind bet), qui augmente avec le temps. À tout moment, un utilisateur peut entrer
"{NomJoueur} wins"pour terminer la partie et enregistrer le vainqueur dans un stockage.Application web. Elle permet aux utilisateurs d'enregistrer les gagnants des parties et affiche un classement. Elle partage le même stockage que l'application en ligne de commande.
Prochaines étapes
Le propriétaire du produit est ravi de l'application en ligne de commande mais préférerait que nous puissions apporter cette fonctionnalité au navigateur. Elle imagine une page web avec une zone de texte permettant à l'utilisateur d'entrer le nombre de joueurs et, lorsqu'il soumet le formulaire, la page affiche la valeur de la mise obligatoire et la met à jour automatiquement au moment approprié. Comme pour l'application en ligne de commande, l'utilisateur peut déclarer le gagnant qui sera enregistré dans la base de données.
À première vue, cela semble assez simple, mais comme toujours, nous devons insister sur l'approche itérative du développement logiciel.
Tout d'abord, nous devrons servir du HTML. Jusqu'à présent, tous nos points de terminaison HTTP ont renvoyé soit du texte brut, soit du JSON. Nous pourrions utiliser les mêmes techniques que nous connaissons (car ce sont toutes des chaînes de caractères au final), mais nous pouvons également utiliser le package html/template pour une solution plus élégante.
Nous devons également pouvoir envoyer de manière asynchrone des messages à l'utilisateur disant La mise obligatoire est maintenant *y* sans avoir à rafraîchir le navigateur. Nous pouvons utiliser les WebSockets pour faciliter cela.
WebSocket est un protocole de communication informatique, fournissant des canaux de communication full-duplex via une seule connexion TCP
Étant donné que nous adoptons un certain nombre de techniques, il est encore plus important que nous fassions d'abord le minimum de travail utile possible, puis que nous itérions.
Pour cette raison, la première chose que nous ferons sera de créer une page web avec un formulaire permettant à l'utilisateur d'enregistrer un gagnant. Au lieu d'utiliser un formulaire simple, nous utiliserons WebSockets pour envoyer ces données à notre serveur afin qu'il puisse les enregistrer.
Après cela, nous travaillerons sur les alertes de mise obligatoire, à quel moment nous aurons mis en place un peu de code d'infrastructure.
Qu'en est-il des tests pour le JavaScript ?
Il y aura du JavaScript écrit pour cela, mais je n'entrerai pas dans l'écriture de tests.
C'est bien sûr possible, mais pour des raisons de concision, je n'inclurai aucune explication à ce sujet.
Désolé. Faites pression sur O'Reilly pour qu'ils me payent pour créer un "Learn JavaScript with tests".
Écrivez d'abord le test
La première chose que nous devons faire est de servir du HTML aux utilisateurs lorsqu'ils accèdent à /game.
Voici un rappel du code pertinent dans notre serveur web :
La chose la plus simple que nous puissions faire pour l'instant est de vérifier que lorsque nous faisons un GET /game, nous obtenons un 200.
Essayez d'exécuter le test
Écrivez suffisamment de code pour le faire passer
Notre serveur a un routeur configuré, il est donc relativement facile à corriger.
Ajoutez à notre routeur :
Et écrivez ensuite la méthode game :
Refactorisation
Le code du serveur est déjà bien structuré grâce au fait que nous insérons plus de code dans le code existant bien factorisé très facilement.
Nous pouvons nettoyer un peu le test en ajoutant une fonction d'aide newGameRequest pour faire la requête à /game. Essayez de l'écrire vous-même.
Vous remarquerez également que j'ai changé assertStatus pour accepter response plutôt que response.Code car je trouve que c'est plus lisible.
Maintenant, nous devons faire en sorte que le point de terminaison renvoie du HTML, le voici :
Nous avons une page web très simple :
Une entrée de texte pour que l'utilisateur puisse saisir le gagnant
Un bouton sur lequel il peut cliquer pour déclarer le gagnant
Du JavaScript pour ouvrir une connexion WebSocket à notre serveur et gérer l'appui sur le bouton de soumission
WebSocket est intégré à la plupart des navigateurs modernes, nous n'avons donc pas à nous soucier d'importer des bibliothèques. La page web ne fonctionnera pas pour les navigateurs plus anciens, mais c'est acceptable pour ce scénario.
Comment tester que nous renvoyons le bon balisage ?
Il y a plusieurs façons. Comme cela a été souligné tout au long du livre, il est important que les tests que vous écrivez aient une valeur suffisante pour justifier le coût.
Écrire un test basé sur un navigateur, en utilisant quelque chose comme Selenium. Ces tests sont les plus "réalistes" de toutes les approches car ils démarrent un vrai navigateur web et simulent un utilisateur interagissant avec lui. Ces tests peuvent vous donner beaucoup de confiance que votre système fonctionne, mais ils sont plus difficiles à écrire que les tests unitaires et beaucoup plus lents à exécuter. Pour les besoins de notre produit, c'est exagéré.
Faire une correspondance exacte de chaîne. Cela peut être correct, mais ces types de tests finissent par être très fragiles. Dès que quelqu'un change le balisage, vous aurez un test qui échoue alors qu'en pratique rien n'est réellement cassé.
Vérifier que nous appelons le bon modèle. Nous utiliserons une bibliothèque de modèles de la bibliothèque standard pour servir le HTML (discuté sous peu) et nous pourrions injecter la chose pour générer le HTML et espionner son appel pour vérifier que nous le faisons correctement. Cela aurait un impact sur la conception de notre code mais ne teste pas grand-chose ; seulement que nous l'appelons avec le bon fichier de modèle. Étant donné que nous n'aurons qu'un seul modèle dans notre projet, le risque d'échec semble faible.
Donc, dans le livre "Learn Go with Tests" pour la première fois, nous n'allons pas écrire de test.
Placez le balisage dans un fichier appelé game.html
Ensuite, modifiez le point de terminaison que nous venons d'écrire comme suit :
html/template est un package Go pour créer du HTML. Dans notre cas, nous appelons template.ParseFiles, en donnant le chemin de notre fichier html. En supposant qu'il n'y ait pas d'erreur, vous pouvez ensuite Execute le modèle, qui l'écrit dans un io.Writer. Dans notre cas, nous voulons qu'il Write sur internet, nous lui donnons donc notre http.ResponseWriter.
Comme nous n'avons pas écrit de test, il serait prudent de tester manuellement notre serveur web juste pour s'assurer que les choses fonctionnent comme nous l'espérions. Allez dans cmd/webserver et exécutez le fichier main.go. Visitez http://localhost:5000/game.
Vous devriez avoir obtenu une erreur concernant l'impossibilité de trouver le modèle. Vous pouvez soit modifier le chemin pour qu'il soit relatif à votre dossier, soit avoir une copie du fichier game.html dans le répertoire cmd/webserver. J'ai choisi de créer un lien symbolique (ln -s ../../game.html game.html) vers le fichier à la racine du projet afin que si je fais des modifications, elles soient reflétées lors de l'exécution du serveur.
Si vous effectuez cette modification et exécutez à nouveau, vous devriez voir notre interface utilisateur.
Maintenant, nous devons tester que lorsque nous recevons une chaîne via une connexion WebSocket à notre serveur, nous la déclarons comme gagnante d'une partie.
Écrivez d'abord le test
Pour la première fois, nous allons utiliser une bibliothèque externe afin de pouvoir travailler avec les WebSockets.
Exécutez go get github.com/gorilla/websocket
Cela récupérera le code de l'excellente bibliothèque Gorilla WebSocket. Maintenant, nous pouvons mettre à jour nos tests pour notre nouvelle exigence.
Assurez-vous que vous avez un import pour la bibliothèque websocket. Mon IDE l'a fait automatiquement pour moi, le vôtre devrait le faire aussi.
Pour tester ce qui se passe depuis le navigateur, nous devons ouvrir notre propre connexion WebSocket et y écrire.
Nos tests précédents concernant notre serveur se contentaient d'appeler des méthodes sur notre serveur, mais maintenant nous devons avoir une connexion persistante à notre serveur. Pour ce faire, nous utilisons httptest.NewServer qui prend un http.Handler et le démarrera pour écouter les connexions.
En utilisant websocket.DefaultDialer.Dial, nous essayons de nous connecter à notre serveur, puis nous essayerons d'envoyer un message avec notre winner.
Enfin, nous affirmons sur le magasin de joueurs pour vérifier que le gagnant a été enregistré.
Essayez d'exécuter le test
Nous n'avons pas modifié notre serveur pour accepter les connexions WebSocket sur /ws, nous ne faisons donc pas encore de poignée de main.
Écrivez suffisamment de code pour le faire passer
Ajoutez une autre entrée à notre routeur :
Puis ajoutez notre nouveau gestionnaire webSocket :
Pour accepter une connexion WebSocket, nous Upgrade la requête. Si vous réexécutez maintenant le test, vous devriez passer à l'erreur suivante.
Maintenant que nous avons ouvert une connexion, nous voudrons écouter un message puis l'enregistrer comme gagnant.
(Oui, nous ignorons beaucoup d'erreurs pour l'instant !)
conn.ReadMessage() bloque en attendant un message sur la connexion. Une fois que nous en obtenons un, nous l'utilisons pour RecordWin. Cela fermerait finalement la connexion WebSocket.
Si vous essayez d'exécuter le test, il échoue toujours.
Le problème est le timing. Il y a un délai entre notre connexion WebSocket lisant le message et enregistrant la victoire, et notre test se termine avant que cela ne se produise. Vous pouvez tester cela en mettant un court time.Sleep avant l'assertion finale.
Allons-y pour l'instant mais reconnaissons que mettre des pauses arbitraires dans les tests est une très mauvaise pratique.
Refactorisation
Nous avons commis de nombreux péchés pour faire fonctionner ce test, à la fois dans le code du serveur et dans le code de test, mais souvenez-vous que c'est la façon la plus simple de travailler.
Nous avons un logiciel désagréable, horrible, mais fonctionnel soutenu par un test, donc maintenant nous sommes libres de l'améliorer et de savoir que nous ne casserons rien accidentellement.
Commençons par le code du serveur.
Nous pouvons déplacer l'upgrader vers une valeur privée à l'intérieur de notre package car nous n'avons pas besoin de le redéclarer à chaque demande de connexion WebSocket :
Notre appel à template.ParseFiles("game.html") s'exécutera à chaque GET /game, ce qui signifie que nous irons sur le système de fichiers à chaque requête alors que nous n'avons pas besoin de réanalyser le modèle. Refactorisons notre code pour que nous analysions le modèle une fois dans NewPlayerServer à la place. Nous devrons faire en sorte que cette fonction puisse maintenant renvoyer une erreur au cas où nous aurions des problèmes pour récupérer le modèle sur le disque ou l'analyser.
Voici les modifications pertinentes à PlayerServer :
En changeant la signature de NewPlayerServer, nous avons maintenant des problèmes de compilation. Essayez de les résoudre vous-même ou référez-vous au code source si vous avez des difficultés.
Pour le code de test, j'ai créé un helper appelé mustMakePlayerServer(t *testing.T, store PlayerStore) *PlayerServer pour pouvoir cacher le bruit d'erreur des tests.
De même, j'ai créé un autre helper mustDialWS pour pouvoir cacher le bruit d'erreur désagréable lors de la création de la connexion WebSocket.
Enfin, dans notre code de test, nous pouvons créer un helper pour faciliter l'envoi de messages :
Maintenant que les tests passent, essayez d'exécuter le serveur et déclarez quelques gagnants dans /game. Vous devriez les voir enregistrés dans /league. N'oubliez pas qu'à chaque fois que nous obtenons un gagnant, nous fermons la connexion, vous devrez actualiser la page pour ouvrir à nouveau la connexion.
Nous avons créé un formulaire web trivial qui permet aux utilisateurs d'enregistrer le gagnant d'un jeu. Itérons dessus pour permettre à l'utilisateur de démarrer une partie en fournissant un nombre de joueurs, et le serveur enverra des messages au client les informant de la valeur de la mise aveugle au fil du temps.
Mettez d'abord à jour game.html pour mettre à jour notre code côté client pour les nouvelles exigences :
Les principaux changements sont l'introduction d'une section pour entrer le nombre de joueurs et d'une section pour afficher la valeur de la mise aveugle. Nous avons une petite logique pour afficher/masquer l'interface utilisateur en fonction de l'étape du jeu.
Tout message que nous recevons via conn.onmessage est supposé être des alertes aveugles, nous définissons donc le blindContainer.innerText en conséquence.
Comment envoyer les alertes aveugles ? Dans le chapitre précédent, nous avons introduit l'idée de Game pour que notre code CLI puisse appeler un Game et tout le reste serait pris en charge, y compris la programmation des alertes aveugles. Cela s'est avéré être une bonne séparation des préoccupations.
Lorsque l'utilisateur était invité dans le CLI à saisir le nombre de joueurs, le système Start démarrait le jeu, ce qui lançait les alertes aveugles, et lorsque l'utilisateur déclarait le gagnant, ils Finish. Ce sont les mêmes exigences que nous avons maintenant, juste une façon différente d'obtenir les entrées ; nous devrions donc chercher à réutiliser ce concept si nous le pouvons.
Notre implémentation "réelle" de Game est TexasHoldem :
En envoyant un BlindAlerter, TexasHoldem peut programmer des alertes aveugles à envoyer où que ce soit :
Et pour rappel, voici notre implémentation du BlindAlerter que nous utilisons dans le CLI :
Cela fonctionne dans le CLI car nous voulons toujours envoyer les alertes à os.Stdout, mais cela ne fonctionnera pas pour notre serveur web. Pour chaque requête, nous obtenons un nouveau http.ResponseWriter que nous mettons ensuite à niveau vers *websocket.Conn. Nous ne pouvons donc pas savoir lors de la construction de nos dépendances où nos alertes doivent aller.
Pour cette raison, nous devons changer BlindAlerter.ScheduleAlertAt pour qu'il prenne une destination pour les alertes afin que nous puissions le réutiliser dans notre serveur web.
Ouvrez blind_alerter.go et ajoutez le paramètre à io.Writer :
L'idée d'un StdoutAlerter ne correspond pas à notre nouveau modèle, donc renommez-le simplement en Alerter :
Si vous essayez de compiler, cela échouera dans TexasHoldem car il appelle ScheduleAlertAt sans destination, pour que les choses se compilent à nouveau pour l'instant, codez en dur os.Stdout.
Essayez d'exécuter les tests et ils échoueront car SpyBlindAlerter n'implémente plus BlindAlerter, corrigez cela en mettant à jour la signature de ScheduleAlertAt, exécutez les tests et nous devrions toujours être au vert.
Il n'est pas logique que TexasHoldem sache où envoyer les alertes aveugles. Mettons maintenant à jour Game pour que lorsque vous commencez une partie, vous déclariez où les alertes doivent aller.
Laissez le compilateur vous dire ce que vous devez corriger. Le changement n'est pas si mauvais :
Mettez à jour
TexasHoldempour qu'il implémente correctementGameDans
CLIquand nous commençons le jeu, passez notre propriétéout(cli.game.Start(numberOfPlayers, cli.out))Dans le test de
TexasHoldem, j'utilisegame.Start(5, io.Discard)pour résoudre le problème de compilation et configurer la sortie d'alerte pour être jetée
Si vous avez bien fait les choses, tout devrait être au vert ! Maintenant, nous pouvons essayer d'utiliser Game dans Server.
Écrivez d'abord le test
Les exigences de CLI et de Server sont les mêmes ! C'est juste le mécanisme de livraison qui est différent.
Jetons un coup d'œil à notre test CLI pour nous inspirer.
Il semble que nous devrions pouvoir aboutir à un résultat similaire en utilisant GameSpy.
Remplacez l'ancien test websocket par ce qui suit :
Comme discuté, nous créons un espion
Gameet le transmettons àmustMakePlayerServer(assurez-vous de mettre à jour l'assistant pour prendre cela en charge).Nous envoyons ensuite les messages websocket pour une partie.
Enfin, nous vérifions que le jeu est démarré et terminé avec ce que nous attendons.
Essayez d'exécuter le test
Vous aurez un certain nombre d'erreurs de compilation autour de mustMakePlayerServer dans d'autres tests. Introduisez une variable non exportée dummyGame et utilisez-la dans tous les tests qui ne se compilent pas :
La dernière erreur est là où nous essayons de passer Game à NewPlayerServer mais il ne le supporte pas encore :
Écrivez le minimum de code pour que le test s'exécute et vérifiez la sortie du test échoué
Ajoutez-le simplement comme argument pour l'instant juste pour faire fonctionner le test :
Enfin !
Écrivez suffisamment de code pour le faire passer
Nous devons ajouter Game comme champ à PlayerServer pour qu'il puisse l'utiliser lorsqu'il reçoit des requêtes.
(Nous avons déjà une méthode appelée game donc renommez-la en playGame)
Ensuite, attribuons-la dans notre constructeur :
Maintenant, nous pouvons utiliser notre Game dans webSocket.
Hourra ! Les tests passent.
Nous n'allons pas envoyer les messages d'alerte aveugle pour l'instant car nous devons y réfléchir. Quand nous appelons game.Start, nous envoyons io.Discard qui va simplement jeter tous les messages qui lui sont écrits.
Pour l'instant, démarrez le serveur web. Vous devrez mettre à jour le main.go pour passer un Game au PlayerServer :
Sans tenir compte du fait que nous ne recevons pas encore d'alertes aveugles, l'application fonctionne ! Nous avons réussi à réutiliser Game avec PlayerServer et il a pris en charge tous les détails. Une fois que nous aurons trouvé comment envoyer nos alertes aveugles à travers les websockets plutôt que de les jeter, cela devrait tout fonctionner.
Mais avant cela, nettoyons un peu le code.
Refactorisation
La façon dont nous utilisons les WebSockets est assez basique et la gestion des erreurs est assez naïve, j'ai donc voulu encapsuler cela dans un type juste pour supprimer ce désordre du code du serveur. Nous pourrons peut-être le revoir plus tard, mais pour l'instant, cela va un peu nettoyer les choses :
Maintenant, le code du serveur est un peu simplifié :
Une fois que nous aurons trouvé comment ne pas jeter les messages d'alerte aveugle, nous aurons terminé.
N'écrivons pas de test !
Parfois, lorsque nous ne sommes pas sûrs de la façon de faire quelque chose, il est préférable de jouer et d'essayer des choses ! Assurez-vous que votre travail est d'abord validé car une fois que nous aurons trouvé un moyen, nous devrions le faire passer par un test.
La ligne de code problématique que nous avons est :
Nous devons passer un io.Writer pour que le jeu puisse y écrire les alertes aveugles.
Ne serait-il pas agréable si nous pouvions passer notre playerServerWS d'avant ? C'est notre enveloppe autour de notre WebSocket, donc il semble que nous devrions pouvoir l'envoyer à notre Game pour envoyer des messages.
Essayez :
Le compilateur se plaint :
Il semble que la chose évidente à faire serait de faire en sorte que playerServerWS implémente io.Writer. Pour ce faire, nous utilisons le *websocket.Conn sous-jacent pour utiliser WriteMessage pour envoyer le message à travers le websocket :
Cela semble trop facile ! Essayez d'exécuter l'application et voyez si elle fonctionne.
Auparavant, modifiez TexasHoldem pour que le temps d'incrémentation des blinds soit plus court afin que vous puissiez le voir en action :
Vous devriez le voir fonctionner ! Le montant de la mise aveugle s'incrémente dans le navigateur comme par magie.
Maintenant, revenons au code et réfléchissons à la façon de le tester. Pour l'implémenter, tout ce que nous avons fait était de passer à StartGame playerServerWS plutôt que io.Discard, ce qui pourrait vous faire penser que nous devrions peut-être espionner l'appel pour vérifier qu'il fonctionne.
L'espionnage est génial et nous aide à vérifier les détails d'implémentation, mais nous devrions toujours essayer de favoriser le test du vrai comportement si nous le pouvons, car lorsque vous décidez de refactoriser, ce sont souvent les tests d'espionnage qui commencent à échouer car ils vérifient généralement les détails d'implémentation que vous essayez de changer.
Notre test ouvre actuellement une connexion websocket à notre serveur en cours d'exécution et envoie des messages pour lui faire faire des choses. De même, nous devrions pouvoir tester les messages que notre serveur renvoie via la connexion websocket.
Écrivez d'abord le test
Nous allons modifier notre test existant.
Actuellement, notre GameSpy n'envoie aucune donnée à out lorsque vous appelez Start. Nous devrions le modifier pour que nous puissions le configurer pour envoyer un message en conserve, puis nous pouvons vérifier que ce message est envoyé au websocket. Cela devrait nous donner confiance que nous avons configuré les choses correctement tout en exerçant le comportement réel que nous voulons.
Ajoutez le champ BlindAlert.
Mettez à jour GameSpy Start pour envoyer le message en conserve à out.
Cela signifie maintenant que lorsque nous exerçons PlayerServer lorsqu'il essaie de Start le jeu, il devrait finir par envoyer des messages via le websocket si les choses fonctionnent correctement.
Enfin, nous pouvons mettre à jour le test :
Nous avons ajouté un
wantedBlindAlertet configuré notreGameSpypour l'envoyer àoutsiStartest appelé.Nous espérons qu'il est envoyé dans la connexion websocket, nous avons donc ajouté un appel à
ws.ReadMessage()pour attendre qu'un message soit envoyé, puis vérifier que c'est celui que nous attendions.
Essayez d'exécuter le test
Vous devriez constater que le test est bloqué indéfiniment. C'est parce que ws.ReadMessage() bloquera jusqu'à ce qu'il obtienne un message, ce qui n'arrivera jamais.
Écrivez le minimum de code pour que le test s'exécute et vérifiez la sortie du test échoué
Nous ne devrions jamais avoir de tests qui se bloquent, donc introduisons un moyen de gérer le code que nous voulons temporiser.
Ce que fait within, c'est prendre une fonction assert comme argument puis l'exécuter dans une go routine. Si/Quand la fonction se termine, elle signalera qu'elle a terminé via le canal done.
Pendant ce temps, nous utilisons une instruction select qui nous permet d'attendre qu'un canal envoie un message. À partir de là, c'est une course entre la fonction assert et time.After qui enverra un signal lorsque la durée sera écoulée.
Enfin, j'ai créé une fonction d'aide pour notre assertion juste pour rendre les choses un peu plus propres :
Voici comment se lit maintenant le test :
Maintenant, si vous exécutez le test...
Écrivez suffisamment de code pour le faire passer
Enfin, nous pouvons maintenant changer notre code serveur, pour qu'il envoie notre connexion WebSocket au jeu quand il démarre :
Refactorisation
Le code du serveur était un très petit changement, il n'y a donc pas beaucoup à changer ici, mais le code de test contient toujours un appel time.Sleep car nous devons attendre que notre serveur fasse son travail de manière asynchrone.
Nous pouvons refactoriser nos helpers assertGameStartedWith et assertFinishCalledWith pour qu'ils puissent réessayer leurs assertions pendant une courte période avant d'échouer.
Voici comment vous pouvez le faire pour assertFinishCalledWith et vous pouvez utiliser la même approche pour l'autre helper :
Voici comment retryUntil est défini :
Récapitulation
Notre application est maintenant complète. Un jeu de poker peut être lancé via un navigateur web et les utilisateurs sont informés de la valeur de la mise aveugle au fil du temps via WebSockets. Lorsque la partie se termine, ils peuvent enregistrer le gagnant qui est persisté en utilisant le code que nous avons écrit il y a quelques chapitres. Les joueurs peuvent découvrir qui est le meilleur (ou le plus chanceux) joueur de poker en utilisant le point de terminaison /league du site web.
Au cours du développement de cette application, nous avons commis des erreurs, mais avec le flux TDD, nous n'avons jamais été très loin d'un logiciel fonctionnel. Nous étions libres de continuer à itérer et à expérimenter.
Le dernier chapitre fera une rétrospective sur l'approche, la conception à laquelle nous sommes arrivés et réglera certains détails.
Nous avons couvert plusieurs choses dans ce chapitre :
WebSockets
Une façon pratique d'envoyer des messages entre les clients et les serveurs qui ne nécessite pas que le client interroge constamment le serveur. Le code client et serveur que nous avons est très simple.
Trivial à tester, mais vous devez faire attention à la nature asynchrone des tests.
Gérer le code dans les tests qui peut être retardé ou ne jamais se terminer
Créez des fonctions d'aide pour réessayer les assertions et ajouter des délais d'expiration.
Nous pouvons utiliser des goroutines pour garantir que les assertions ne bloquent rien, puis utiliser des canaux pour leur permettre de signaler qu'elles ont terminé, ou non.
Le package
timecontient des fonctions utiles qui envoient également des signaux via des canaux sur des événements dans le temps afin que nous puissions définir des délais d'expiration.
Mis à jour