Rx: Exécuter une action de manière asynchrone
By Michael DELVA on Thursday 13 May 2010, 11:00 - C# - Permalink
TweetPour clore le chapitre sur l'utilisation de Rx pour mon application de gestion de mises à jour, je vais terminer comme annoncé par vous montrer comment je lance les exécutables téléchargés de manière asynchrone, afin de ne pas bloquer le thread de la vue pendant l'installation.
Commençons tout d'abord par un petit rappel de l'interface utilisée par la classe AssemblyUpdater:
public interface IFileInstaller
{
IObservable<Unit> Install(string filePath);
}
La manière dont on a utilisé jusque là les IObservable<Unit> était d'informer les observateurs de la fin de l'observable, étant donné que le type Unit ne transporte pas de données (c'est un void qui s'ignore). Le souci ici, c'est que si on suit cette logique, on informera la vue d'un changement uniquement à la fin de l'installation de chaque update. Ce qui n'est pas très pratique si vous voulez avoir la possibilité d'informer l'utilisateur qu'une installation est en cours. Mais ce n'est heureusement pas un problème du tout, puisqu'on pourra très bien, en utilisant Observable.Create<Unit>, appeler la fonction OnNext de l'observer avant de réellement exécuter la mise à jour.
De cette façon, on va commencer par informer la vue qu'une installation va débuter, ce qui va lui permettre par exemple d'afficher une progressbar avec la propriété IsIndeterminate définie à True.
Mais voyons sans plus attendre l'implémentation de notre interface:
public class FileInstaller : IFileInstaller
{
private static IObservable<Unit> DoInstall(string filePath)
{
return Observable.Create<Unit>(...);
}
public IObservable<Unit> Install(string filePath)
{
return Observable.ToAsync(() => DoInstall(filePath).First())();
}
}
Comme vous pouvez le constater, il est très simple d'exécuter une fonction en asynchrone: il suffit d'utiliser Observable.ToASync(), qui prend comme argument dans notre cas un paramètre de type Func<IObservable<Unit>> pour retourner un résultat de même type. Par défaut, ToAsync va exécuter le code contenu dans le Func passé en paramètre dans un nouveau thread (si on ne spécifie pas le paramètre IScheduler, celui-ci prend Scheduler.TaskPool comme valeur par défaut).
Vous noterez également l'appel à la méthode d'extension First() sur l'observable retourné par DoInstall(), ceci afin d'utiliser sa propriété bloquante pour ne quitter ToAsync que lorsque l'installation est terminée. (Vous aurez remarqué également la fin de ligne très lisible avec toutes ces parenthèses :x)
Et pour terminer, la définition de DoInstall, avec notamment l'appel à OnNext avant le lancement de la mise à jour:
private static IObservable<Unit> DoInstall(string filePath)
{
return Observable.Create<Unit>(observer =>
{
using (Process process = new Process())
{
try
{
observer.OnNext(new Unit());
process.StartInfo.FileName = filePath;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForExit();
int exitCode = process.ExitCode;
process.Close();
if (exitCode != 0)
observer.OnError(new UpdateExecutionException(Resources.Resources.FileInstallerPrematureExit));
else
observer.OnCompleted();
}
catch (Win32Exception e)
{
const int errorAccessDenied = 5;
observer.OnError(e.NativeErrorCode == errorAccessDenied
? new UpdateExecutionException(Resources.Resources.FileInstallerPermissions, Resources.Resources.AccessDeniedError)
: new UpdateExecutionException(Resources.Resources.FileInstallerUnknownError, e));
}
}
return () => { };
});
}
Rien de bien compliqué donc :)
Et voilà qui conclue de fort belle manière ma foi (je suis objectif si je veux) cette petite série d'articles autour de l'utilisation de Rx dans un contexte concret.
Soyez sûr en tout cas de retrouver encore d'autres articles autour de cette fantastique technologie (au moins 1 article est prévu pour les jours qui viennent)
A bientôt !!!