Javascript intégré Error
fournit des informations utiles, mais peut souvent manquer de clarté. Une taille unique est idéale pour la langue, mais nous pouvons faire mieux dans nos propres applications. C’est là que les erreurs personnalisées entrent en jeu.
Vous avez peut-être vu des erreurs personnalisées lors de l’utilisation de Node.js. Les types d’erreur intégrés au nœud sont tels que AssertionError
, RangeError
, ReferenceError
, SyntaxError
, et SystemError
sont toutes des extensions de la classe native Error
.
Utiliser Error
comme base vous donne toute la puissance de l’implémentation de Javascript ainsi que des avantages supplémentaires. En voici quelques-uns:
- Formats de messages d’erreur personnalisés.
- Propriétés supplémentaires sur l’objet Error.
- Erreurs nommées pour faciliter le débogage et la gestion des erreurs conditionnelles.
- Erreurs cohérentes pour les consommateurs de bibliothèques à référencer.
Extension de la classe d’erreur
Pour commencer, utilisons un exemple générique. Nous allons étendre la classe Error
et utiliser super
pour hériter de ses fonctions.
class CustomError extends Error { constructor(...params) { super(...params) // We're spreading `params` as a way to bring all of `Error`'s functionality in. }}
Maintenant, lorsque vous throw
une erreur, vous pouvez le faire avec throw new CustomError("Something went wrong...")
. Il vous donne également la possibilité de vérifier les erreurs par rapport au type:
try { throw new CustomError("Something went wrong")} catch (error) { if (error instance of CustomError) { // do something specifically for that type of error }}
Cela seul ne fait pas grand-chose, à part vous donner un nouveau type d’erreur à appeler et à vérifier. Pour mieux comprendre ce qui peut être fait, regardons ce qui est standard sur Error
.
- Error.name Erreur
- .erreur du message
- .prototype.toString()
- Erreur.prototype.constructeur()
Il n’y a pas grand chose avec lequel travailler? Mis à part le constructeur et la méthode toString
, le nom de l’erreur et le message décrivant l’erreur sont les seules parties que nous sommes susceptibles d’utiliser. Dans l’exemple ci-dessus, message
est défini en passant « quelque chose s’est mal passé » comme argument lors de l’instanciation de l’erreur.
Heureusement, la plupart des plates-formes javascript aiment le navigateur et le nœud.js a ajouté ses propres méthodes et propriétés en plus de celles répertoriées. Nous nous concentrerons sur quelques aspects de l’implémentation des erreurs de la V8, le moteur Javascript alimentant Chrome et Node.js.
Les deux zones d’intérêt sont stack
et captureStackTrace
. Comme leur nom l’indique, ils vous permettent de faire surface sur la trace de la pile. Voyons à quoi cela ressemble avec un exemple.
class CustomError extends Error { constructor(...args) { super(...args) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError) } this.name = "Our Custom Error" }}try { throw new CustomError("Something went wrong")} catch (err) { console.error(err.stack)}
Dans cet exemple, nous appelons Error.captureStackTrace
, si la plate-forme le prend en charge, pour nous assurer qu’une trace complète est ajoutée à notre erreur personnalisée. Nous throw
l’erreur de l’intérieur du bloc try
, qui passera le contrôle au bloc catch
.
Si vous exécutez le code ci-dessus, vous verrez que la première ligne de stack
est celle de l’erreur name
et message
.
Our Custom Error: Something went wrong
Cette erreur n’est pas très « profonde », donc la pile est principalement un nœud.js internes. Chaque ligne est un « cadre » de la pile. Ils contiennent des détails sur l’emplacement de l’erreur dans la base de code.
Maintenant que nous savons comment créer et appeler une erreur personnalisée, rendons-la utile.
Personnalisation du contenu de l’erreur
L’erreur personnalisée avec laquelle nous avons travaillé est correcte, mais modifions-la pour la rendre plus utile. La classe sera CircuitError
. Nous l’utiliserons pour fournir des erreurs personnalisées provenant d’un disjoncteur. Si vous n’êtes pas familier avec le modèle, ce n’est pas grave. Les détails de mise en œuvre du disjoncteur ne sont pas importants. Ce qui est important, c’est que nous puissions transmettre certaines informations à l’erreur et qu’elle présente ces informations à l’utilisateur.
Un disjoncteur a un état « OUVERT » qui ne permet à rien de le traverser pendant une durée déterminée. Définissons notre CircuitError
pour contenir certains détails qui pourraient être utiles aux fonctions qui le reçoivent lorsque l’état est OPEN
.
class CircuitError extends Error { // 1 constructor(state, nextAttempt, ...params) { super(...params) if (Error.captureStackTrace) { Error.captureStackTrace(this, CircuitError) } this.name = "CircuitError" this.state = state // 2 this.message = `The Circuit is ${state}.` // 3 if (nextAttempt) { this.timestamp = Date.now() this.nextAttempt = nextAttempt this.message += ` Next attempt can be made in ${this.nextAttempt - this.timestamp}ms.` } }}
Notre erreur mise à jour fait quelques nouvelles choses, toutes dans le constructeur. Les arguments passés ont changé (1) pour inclure le state
du disjoncteur, ainsi qu’un horodatage nextAttempt
pour indiquer quand le disjoncteur recommencera à essayer des requêtes.
Il définit ensuite quelques nouvelles propriétés et met à jour le message en fonction des valeurs présentées. Nous pouvons le tester en lançant une nouvelle version de cette erreur.
try { throw new CircuitError("OPEN", Date.now() + 8000)} catch (err) { console.error(err)}
Maintenant, lorsque nous lançons l’erreur, elle prend l’état comme premier argument et un horodatage comme second. Pour cette démo, nous passons dans le temps 8000 millisecondes dans le futur. Notez que nous enregistrons également l’erreur elle-même, plutôt que simplement la pile.
L’exécution du code entraînera quelque chose comme ce qui suit:
CircuitError : The Circuit is OPEN. Next attempt can be made in 6000ms. at Object.<anonymous> (/example.js:21:9) at Module._compile (internal/modules/cjs/loader.js:1063:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1103:10) at Module.load (internal/modules/cjs/loader.js:914:32) at Function.Module._load (internal/modules/cjs/loader.js:822:14) at Function.Module.runMain (internal/modules/cjs/loader.js:1143:12) at internal/main/run_main_module.js:16:11 { name: 'Circuit Error', state: 'OPEN', message: 'The Circuit is OPEN. Next attempt can be made in 6000ms.', timestamp: 1580242308919, nextAttempt: 1580242314919}
Le résultat est le nom de l’erreur suivi du message sur la première ligne. Ensuite, nous avons le reste de la pile, suivi de toutes les propriétés que nous avons définies dans le CircuitError
. Nos consommateurs de cette erreur pourraient utiliser ces données pour réagir à l’erreur. Par exemple:
try { throw new CircuitError("OPEN", Date.now() + 8000)} catch (err) { customLogger(err) if (err.state === "OPEN") { handleOpenState(err.nextAttempt) }}
Au lieu d’une erreur générique, nous avons maintenant quelque chose de mieux adapté aux besoins de notre application.
Réagir aux pannes
Les erreurs personnalisées sont un excellent moyen de réagir aux pannes. Il est courant de supposer que toutes les erreurs sont inattendues, mais en fournissant des erreurs utiles, vous pouvez rendre leur existence plus facile à gérer. En utilisant des erreurs personnalisées, non seulement nous pouvons fournir aux utilisateurs de nos applications des données précieuses, mais nous offrons également la possibilité de répondre aux types d’erreurs via la condition instance of
.
Chez Bearer, nous utilisons des erreurs personnalisées pour standardiser les réponses dans notre code, fournir uniquement les informations dont vous avez besoin chez notre client et rendre nos erreurs exploitables.
Comment utilisez-vous les erreurs personnalisées dans vos applications ? Connectez-vous avec nous @BearerSH et faites-le nous savoir.