Pourquoi devrais-je créer des opérations WebAPI asynchrones plutôt que synchronisées?

J’ai l’opération suivante dans une API Web que j’ai créée:

// GET api/ [HttpGet] [Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")] public CartTotalsDTO GetProductsWithHistory(Guid pharmacyId, int page, ssortingng filter = null ,[FromUri] bool refresh = false) { return delegateHelper.GetProductsWithHistory(CustomerContext.Current.GetContactById(pharmacyId), refresh); } 

L’appel à ce webservice se fait par un appel Jquery Ajax de cette façon:

 $.ajax({ url: "/api/products/pharmacies//page/" + vm.currentPage() + "/" + filter, type: "GET", dataType: "json", success: function (result) { vm.items([]); var data = result.Products; vm.totalUnits(result.TotalUnits); } }); 

J’ai vu des développeurs implémenter l’opération précédente de cette façon:

 // GET api/ [HttpGet] [Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")] public async Task GetProductsWithHistory(Guid pharmacyId, int page, ssortingng filter = null ,[FromUri] bool refresh = false) { return await Task.Factory.StartNew(() => delegateHelper.GetProductsWithHistory(CustomerContext.Current.GetContactById(pharmacyId), refresh)); } 

Je dois dire que GetProductsWithHistory () est une opération assez longue. Compte tenu de mon problème et de son contexte, en quoi l’opération webAPI sera-t-elle asynchrone?

Dans votre exemple spécifique, l’opération n’est pas du tout asynchrone, vous faites donc de la synchronisation asynchrone. Vous ne faites que libérer un thread et en bloquer un autre. Il n’y a aucune raison à cela, car tous les threads sont des threads de pool de threads (contrairement à une application graphique).

Dans ma discussion sur «async over sync», j’ai fortement suggéré que si vous avez une API qui est implémentée de manière synchrone en interne, vous ne devez pas exposer un homologue asynchrone qui Task.Run simplement la méthode synchrone dans Task.Run .

De Dois-je exposer les wrappers synchrones pour les méthodes asynchrones?

Toutefois, lorsque WebAPI appelle async où il y a une opération asynchrone réelle (généralement des E / S) au lieu de bloquer un thread qui est assis et attend un résultat, le thread revient au pool de threads et peut donc effectuer une autre opération. Dans l’ensemble, cela signifie que votre application peut faire plus avec moins de ressources et améliore l’évolutivité.

Une approche pourrait être (j’ai utilisé cela avec succès dans les applications client) pour avoir un service Windows exécutant les opérations longues avec les threads de travail, puis le faire dans IIS pour libérer les threads jusqu’à ce que l’opération de blocage soit terminée: les résultats sont stockés dans un tableau (lignes identifiées par jobId) et un processus plus propre les nettoyant quelques heures après leur utilisation.

Pour répondre à la question: “Compte tenu de mon problème et de son contexte, en quoi l’opération webAPI sera-t-elle bénéfique?” Étant donné que c’est “une opération assez longue”, je pense à plusieurs secondes plutôt qu’à des ms, cette approche libère les threads IIS. De toute évidence, vous devez également exécuter un service Windows qui, lui-même, prend des ressources, mais cette approche pourrait empêcher un flot de requêtes lentes de voler des threads d’autres parties du système.

 // GET api/ [HttpGet] [Route("pharmacies/{pharmacyId}/page/{page}/{filter?}")] public async Task GetProductsWithHistory(Guid pharmacyId, int page, ssortingng filter = null ,[FromUri] bool refresh = false) { var jobID = Guid.NewGuid().ToSsortingng() var job = new Job { Id = jobId, jobType = "GetProductsWithHistory", pharmacyId = pharmacyId, page = page, filter = filter, Created = DateTime.UtcNow, Started = null, Finished = null, User = {{extract user id in the normal way}} }; jobService.CreateJob(job); var timeout = 10*60*1000; //10 minutes Stopwatch sw = new Stopwatch(); sw.Start(); bool responseReceived = false; do { //wait for the windows service to process the job and build the results in the results table if (jobService.GetJob(jobId).Finished == null) { if (sw.ElapsedMilliseconds > timeout ) throw new TimeoutException(); await Task.Delay(2000); } else { responseReceived = true; } } while (responseReceived == false); //this fetches the results from the temporary results table return jobService.GetProductsWithHistory(jobId); }