Un programme qui ouvre quelque chose doit le refermer : un indicateur de chargement, un verrou, une connexion. La ligne de fermeture arrive toujours après le travail, et une erreur survenue pendant ce travail l'emporte avec elle.
finally répond à ce problème précis, et à aucun autre : il réserve une zone que rien ne peut sauter.
Définition
finally termine une structure try et s'exécute quoi qu'il arrive : travail réussi, erreur rattrapée par un catch, erreur rattrapée par personne, ou sortie anticipée par un return. Aucun autre bloc n'offre cette garantie.
let spinnerVisible = false;
function charger(lecture) {
spinnerVisible = true;
try {
return lecture();
} finally {
spinnerVisible = false;
}
}
try {
charger(() => { throw new Error("réseau indisponible"); });
} catch (erreur) {
console.log(erreur.message, "| spinner encore visible :", spinnerVisible);
}
// réseau indisponible | spinner encore visible : falseL'indicateur est retiré alors même que la lecture a échoué, ce qu'une ligne placée après la structure n'aurait pas garanti.
Il passe même après un return
C'est le comportement qui surprend le plus, et celui qui rend le bloc réellement utile. On imagine volontiers qu'un return met fin à la fonction sur-le-champ. Ce n'est pas ce qui se passe.
function valeur() {
try {
return "venue du try";
} finally {
console.log("le finally passe d'abord");
}
}
console.log(valeur());
// le finally passe d'abord
// venue du tryLe moteur évalue l'expression qui suit le return, met le résultat de côté, exécute le finally, puis rend la valeur. La même mécanique s'applique à break et à continue à l'intérieur d'une boucle.
N'écrivez jamais de return dans le finally. Il écrase la valeur préparée par le try et, bien pire, il fait disparaître une erreur en cours de remontée : la fonction renvoie tranquillement un résultat faux. Un throw écrit là produit le même effacement.
L'ordre exact d'exécution
Quand une structure réunit les trois blocs, dire de tête qui passe avant qui devient hasardeux. Chaque ligne du tableau se lit comme un déroulé dans le temps.
| Situation | Ce qui s'exécute, dans l'ordre |
|---|---|
| Aucune erreur | try, puis finally |
| Erreur rattrapée | try jusqu'à l'erreur, puis catch, puis finally |
| Erreur non rattrapée | try jusqu'à l'erreur, puis finally, puis l'erreur repart |
| Sortie par return | try jusqu'au return, puis finally, puis la valeur part |
La troisième ligne est celle qui compte le plus en production : le nettoyage a lieu avant que l'appelant ne reçoive quoi que ce soit. Un point souvent oublié : une erreur levée à l'intérieur du catch déclenche elle aussi le finally avant de repartir.
Questions fréquentes
Peut-on écrire un finally sans catch ?
Oui, la forme est valide et même très courante. Elle dit quelque chose de précis : cette fonction ne prétend pas traiter l'erreur, elle la laisse remonter vers le code qui saura, mais elle tient à ranger derrière elle avant qu'elle ne parte.
Le bloc s'exécute-t-il si l'erreur n'est rattrapée nulle part ?
Oui, entièrement, puis l'erreur poursuit sa remontée jusqu'à la console ou jusqu'à l'arrêt du processus. Les seules situations qui l'en empêchent échappent au langage : un onglet fermé, un processus tué par le système, une coupure de courant.
Quelle différence avec une ligne écrite après la structure ?
Une ligne placée après le bloc ne s'exécute que si l'exécution arrive jusqu'à elle. Elle est donc sautée par un return, par une erreur non rattrapée, et par une erreur levée depuis le catch. Le finally couvre ces trois chemins, ce qui en fait le seul endroit sûr pour un nettoyage obligatoire.