[Windows 8] Un exemple court pour comprendre ce que sont Promise, then() et done() avec WinJS

27. January 2013 14:01 by Renaud in Windows 8  //  Tags: , , , , ,   //   Comments (0)

Le concept des Promise et la différence entre then() et done() sont assez difficiles à comprendre. Les explications dans la doc MSDN sont abstraites, et on pourrait croire que then() et done() font la même chose... mais ce n'est pas le cas. Voici un petit morceau de code qui, je pense, est plutôt explicite. Copiez/collez-le dans une nouvelle app Windows 8 JS/HTML et placez des breakpoints à chaque ligne, exécutez l'app, et lisez les commentaires. Le but de ce code est également de vous montrer qu'il n'est pas nécessaire avec les Promise d'écrire des fonctions, dans des fonctions, dans des fonctions... En effet, j'ai vu quelques personnes demander en quoi ça facilitait la vie d'utiliser les Promise avec WinJS. Ils demandent cela parce qu'ils pensent qu'ils doivent écrire du code comme cela :

asyncCall().then(function() {
    anotherAsyncCall().then(function(){
        aThirdAsyncCall();
    }
}

Et c'est vrai que dans ce cas, on pourrait se poser des questions sur l'utilité des Promise. On ne fait que réécrire du code avec des callbacks imbriqués pour finir avec un bordel absolu. Evidemment ce n'est pas le cas ! Regardez le code qui suit et lisez les commentaires :)

 WinJS.xhr({ url: "http://www.microsoft.com" })
        .then(function (microsoftResult) {
            // Ici, on reçoit une réponse, comme on s'y attendait.
            // Que faisons-nous ensuite? 
            // On pourrait faire un autre appel async,
            // ou juste retourner une valeur simple pour voir...
            // Retournons une url, que l'on utilisera 
            // par la suite pour faire une nouvelle requête :
            return "http://dev.windows.com";
        }).then(function (devWindowsUrl) {
            // devWindowsUrl contient simplement la valeur retournée dans la partie précédente.
            // Maintenant, essayons un autre appel async
            // et plutôt que d'utiliser des callbacks (then)
            // retournons simplement l'objet Promise !!
            return WinJS.xhr({ url: devWindowsUrl });
        }).then(function (devWindowsResult) {
            // Quand on arrive ici, on peut voir que dewWindowsResult ne contient pas de Promise.
            // Ca contient en fait le résultat de la Promise, c'est à dire la réponse à la requête.
            // C'est donc là que se situe la magie =)

            // Maintenant déclenchons une exception en appelant une fonction inexistante...
            return devWindowsResult.callNonExistingFunction();
        }).then(function (nonExistingResult) {
            // Nous n'atteignons pas ce point-ci...
            return "ha bon, ça existait finalement?";
        }, function (nonExistingError) {
            // Au lieu de ça, on arrive ici, parce que la Promise s'est terminée avec une erreur.
            // Mais c'est pas grave. Vu qu'on est là, on peut gérer cette erreur.
            // On va retourner un message gentil pour rassurer tout le monde.
            return "cette fonction n'existe pas mais c'est ok, on a géré l'exception !";
        }).then(function (errorHasBeenHandledResult) {
            // du coup, tout rentre dans l'ordre et on continue
            return "content =)";
        }).done(function (result) {
            // et pour finir, il faudrait cloturer par un appel à done()
            // La différence est que si une exception est déclenchée
            // à l'intérieur de cette fonction, l'exception sera vraiment lancée
            // et pas simplement passée comme paramètre à un prochain callback

            // L'exception sera lancée de manière asynchrone, donc si elle vous intéresse
            // il faut catcher les exceptions de la dernière chance au niveau de l'app

            // Essayez de dé-commenter cette ligne pour voir.
            //return result.callANonExistingMethod();
        }, function (error) {
            // Ici on peut éventuellement gérer une erreur renvoyée par l'appel asynchrone précédent.
            var resultError = error;
        });

Pour être sûr de catcher les exceptions asynchrone, un petit coup de WinJS pour ajouter un eventhandler au niveau de l'app :

    var app = WinJS.Application;
    app.onerror = function (error) {
        //Log the last-chance exception 
    };

Enfin, sachez que dans le cas où vous terminez par done(), si vous ne fournissez pas de callback pour le onError, et que la Promise se termine avec une erreur, cette erreur sera automatiquement forwardée, et donc une exception de la dernière chance sera déclenchée !

 

[Windows 8] Short code sample to understand Promise, then() and done() with WinJS

27. January 2013 14:01 by Renaud in Windows 8  //  Tags: , , , ,   //   Comments (0)

I just wrote a small piece of code which is, in my opinion, self-explanatory. Copy/past those lines in a blank Windows 8 JS/HTML app and put some break points at each line. Then, follow the story :)

The aim of this example is to show that you don't have to write functions, within functions, within functions.

Indeed, I know some people think they have to write code like this :

asyncCall().then(function() {
    anotherAsyncCall().then(function(){
        aThirdAsyncCall();
    }
}

But then, why would we use Promises ? Is it just another way to write messy code? Of course not ! Look at this code and read the comments :)

 WinJS.xhr({ url: "http://www.microsoft.com" })
        .then(function (microsoftResult) {
            // Here we receive the response, as excepted.
            // So what do we do here? We could try another asynchronous call,
            // or just return a simple value.
            // Let's return the url that we want to fetch in the next call :
            return "http://dev.windows.com";
        }).then(function (devWindowsUrl) {
            // The devWindowsUrl simply contains the value that we returned in the previous call.
            // Now let's try another Async call, and instead of using callbacks here, let's just return the promise !!
            return WinJS.xhr({ url: devWindowsUrl });
        }).then(function (devWindowsResult) {
            // When we are here, you can see that devWindowsResult doesn't contain a promise.
            // It contains the actual value that we wanted to retrieve from the previous async call.
            // That's the magic =)

            // Now let's throw an exception by calling a non-existing function
            return devWindowsResult.callNonExistingFunction();
        }).then(function (nonExistingResult) {
            // We don not hit this breakpoint
            return "wow, does it really exist?";
        }, function (nonExistingError) {
            // Instead, we are here, because the promised is fulfilled with an error
            // Now let's return something nice, because we managed to handle the error.
            return "that function didn't exist but it's okay, we handled the exception!";
        }).then(function (errorHasBeenHandledResult) {
            // then, everything continues normally
            return "happy =)";
        }).done(function (result) {
            // and at the end you should end up with a done function.
            // The difference is that if there is an exception thrown, 
            // within this function, the error will be actually thrown
            // and not just passed as an argument in a next callback.

            // The error is thrown asynchronously, so you should ensure
            // you catch the last-chance exception at the app-level.
            return result.callANonExistingMethod();
        }, function (error) {
            // Here we can eventually handle an error in the previous 'then' error handling callback.
            var resultError = error;
        });

Then, to make sure you handle the exceptions that are thrown asynchronously, you can just add this eventhandler with WinJS :

    var app = WinJS.Application;
    app.onerror = function (error) {
        //Log the last-chance exception 
    };

Finally, if you use a done() function at the end of your async calls, remember that the previous promise could throw an exception. If it is the case, and that you didn't provide a onError callback, the exception will be automatically forwarded and you will end up with a first chance exception !

TextBox

About the author

I'm a developer, blog writer, and author, mainly focused on Microsoft technologies (but not only Smile). I'm Microsoft MVP Client Development since July 2013.

Microsoft Certified Professional

I'm currently working as an IT Evangelist with an awesome team at the Microsoft Innovation Center Belgique, where I spend time and energy helping people to develop their projects. I also give training to enthusiastic developers and organize afterworks with the help of the Belgian community.

MIC Belgique

Take a look at my first book (french only): Développez en HTML 5 pour Windows 8

Développez en HTML5 pour Windows 8

Membre de l'association Fier d'être développeur

TextBox

Month List