Le try/catch est-il, aujourd'hui, aussi performant que des erreurs gérées avec des ifs ?

icon Tags de l'article : ,

Décembre 11, 2019
Ce matin, un collègue m'a montré comment il avait réduit le code d'une méthode en passant par un try/catch (à la place d'un enchainement de if).

Je lui ai expliqué que c'était plutôt à éviter, pour 3 raisons :
  • Moins performant
  • Pas forcément plus clair pour le développeur qui arrivera derrière ("pourquoi il a fait un try/catch ? il y a un cas particulier à gérer ?")
  • Ne permet pas de s'assurer que tous les cas métiers ont été gérés en un seul coup d'oeil

Dans cette situation, les points 2 et 3 ne s'appliquaient pas vraiment, le code étant plutôt simple. Mais le point 1 s'appliquait toujours.

Mon collègue m'a dit qu'après avoir fait des tests de son côté, il s'était rendu compte que l'écart de performances entre un try/catch et un enchainement de ifs était négligeable.

J'ai donc voulu tester ça. #doute

Pour ça, j'ai run 2/3 tests avec le code suivant :

@Component({
    selector: 'app-tab1',
    templateUrl: 'tab1.page.html',
    styleUrls: ['tab1.page.scss']
})
export class Tab1Page {

    nbItems: Number = 100000;
    public resultIf: Number;
    public resultTryCatch: Number;

    constructor() {
        this.doTest();
    }

    doTest() {
        let items = [];
        for(let i = 0; i < this.nbItems ; i++) {
            items.push(this.generateRandomToto())
        }

        // first we try to update the values of the Totos with if
        let startDate = new Date();
        for(let i = 0; i < this.nbItems ; i++) {
            this.testWithIf(items[i]);
        }
        let endDate = new Date();
        this.resultIf = endDate.getTime() - startDate.getTime();

        // then we try with to update the values of the Totos with try/catch
        startDate = new Date();
        for(let i = 0; i < this.nbItems ; i++) {
            this.testWithTryCatch(items[i]);
        }
        endDate = new Date();
        this.resultTryCatch = endDate.getTime() - startDate.getTime();
    }

    generateRandomToto(): Toto {
        if(Math.round(Math.random()) === 0) { // the math.round of a math.random gives 0 or 1
            const result = new Toto();
            result.titi = new Titi();
            result.titi.tutu = 'bonjour';
            return result;
        } else {
            const result = new Toto();
            return result;
        }
    }

    testWithIf(toto: Toto) {
        if(toto && toto.titi && toto.titi.tutu) {
            toto.titi.tutu = 'wesh';
        }
    }

    testWithTryCatch(toto: Toto) {
        try {
            toto.titi.tutu = 'wesh';
        } catch(error) { }
    }
}

class Toto {
    public titi: Titi;
}

class Titi {
    public tutu: string;
}

Maintenant la question : quel écart en fonction du nombre de try/catchs ?

Voici le résultat pour 100 objets : 0ms avec des ifs, 2ms avec des try/catchs.

Woaw ! On a déjà une différence avec seulement 100 try/catchs !

Et si on augmente ?
1 000 objets -> 1ms avec des ifs, 17ms avec des try/catchs.
10 000 objets -> 1ms avec des ifs, 153ms avec des try/catchs.
100 000 objets -> 2ms avec des ifs, 1427ms avec des try/catchs.

Résultat : non, le try/catch n'est pas aussi performant que les ifs. Loin de là.

Même aujourd'hui, à l'aube de 2020, le try/catch ne doit être utilisé que lorsqu'on a pas le choix :
  • pour attraper globalement une erreur
  • lorsqu'on n'a pas le choix (par exemple si l'objet/la couche concernée throw des erreurs)
  • pour gérer un cas particulier

En dehors de ces situations, le try/catch reste à éviter.

Bon dev à tous et toutes !