c# - sirve - ¿Cómo puedo llamar a un método asíncrono desde un método no asíncrono?




return task c# (2)

¿Me estoy perdiendo de algo?

Sí. El código asíncrono, por su naturaleza, implica que el subproceso actual no se utiliza mientras la operación está en curso. El código síncrono, por su naturaleza, implica que el subproceso actual se bloquea mientras la operación está en curso. Esta es la razón por la que llamar código asíncrono desde código síncrono literalmente no tiene sentido. De hecho, como describo en mi blog, un enfoque ingenuo (usando Result / Wait ) puede fácilmente resultar en puntos muertos .

Lo primero a considerar es: ¿ debería mi API ser síncrona o asíncrona? Si se trata de E / S (como en este ejemplo), debería ser asíncrono . Por lo tanto, este sería un diseño más apropiado:

public async Task<string> RetrieveHolidayDatesFromSourceAsync() {
    var result = await this.DoRetrieveHolidayDatesFromSourceAsync();
    /** Do stuff **/
    var returnedResult  = this.TransformResults(result); /** Where result gets used **/
    return returnedResult;
}

Como describo en mi artículo sobre las mejores prácticas de async , debe ir "async todo el camino". Si no lo hace, no obtendrá ningún beneficio de async de todos modos, así que ¿para qué molestarse?

Pero digamos que usted está interesado en ir al final de forma asíncrona, pero en este momento no puede cambiar todo , solo desea cambiar parte de su aplicación. Esa es una situación bastante común.

En ese caso, el enfoque adecuado es exponer tanto las API síncronas como las asíncronas. Finalmente, después de que se actualice todo el otro código, se pueden eliminar las API síncronas. Exploré una variedad de opciones para este tipo de escenario en mi artículo sobre el desarrollo asíncrono de Brownfield ; Mi favorito personal es el "hackeo de parámetros bool", que se vería así:

public string RetrieveHolidayDatesFromSource() {
  return this.DoRetrieveHolidayDatesFromSourceAsync(sync: true).GetAwaiter().GetResult();
}

public Task<string> RetrieveHolidayDatesFromSourceAsync() {
  return this.DoRetrieveHolidayDatesFromSourceAsync(sync: false);
}

private async Task<string> DoRetrieveHolidayDatesFromSourceAsync(bool sync) {
  var result = await this.GetHolidayDatesAsync(sync);
  /** Do stuff **/
  var returnedResult  = this.TransformResults(result);
  return returnedResult;
}

private async Task<string> GetHolidayDatesAsync(bool sync) {
  using (var client = new WebClient()) {
    return sync
        ? client.DownloadString(SourceURI)
        : await client.DownloadStringTaskAsync(SourceURI);
  }
}

Este enfoque evita la duplicación de código y también evita cualquier problema de interbloqueo o reentrada común con otras soluciones antipattern "sync-over-async".

Tenga en cuenta que todavía trataría el código resultante como un "paso intermedio" en la ruta a una API correctamente asíncrona. En particular, el código interno tuvo que recurrir a WebClient (que admite sincronización y asíncrono) en lugar del HttpClient preferido (que solo es compatible con async). Una vez que se haya cambiado todo el código de llamada para usar RetrieveHolidayDatesFromSourceAsync y no RetrieveHolidayDatesFromSource , entonces volveré a visitar esto y eliminaré toda la deuda tecnológica, cambiándolo para usar HttpClient y ser solo async.

https://code.i-harness.com

Esta pregunta ya tiene una respuesta aquí:

Tengo el siguiente método:

    public string RetrieveHolidayDatesFromSource() {
        var result = this.RetrieveHolidayDatesFromSourceAsync();
        /** Do stuff **/
        var returnedResult  = this.TransformResults(result.Result); /** Where result gets used **/
        return returnedResult;
    }


    private async Task<string> RetrieveHolidayDatesFromSourceAsync() {
        using (var httpClient = new HttpClient()) {
            var json = await httpClient.GetStringAsync(SourceURI);
            return json;
        }
    }

Lo anterior no funciona y parece no devolver ningún resultado correctamente. ¿No estoy seguro de dónde me falta una declaración para forzar la espera de un resultado? Quiero que el método RetrieveHolidayDatesFromSource () devuelva una cadena.

Lo que sigue funciona bien, pero es sincrónico y creo que se puede mejorar. Tenga en cuenta que el siguiente es síncrono, por lo que me gustaría cambiar a Asíncrono, pero por alguna razón no puedo envolver mi cabeza.

    public string RetrieveHolidayDatesFromSource() {
        var result = this.RetrieveHolidayDatesFromSourceAsync();
        /** Do Stuff **/

        var returnedResult = this.TransformResults(result); /** This is where Result is actually used**/
        return returnedResult;
    }


    private string RetrieveHolidayDatesFromSourceAsync() {
        using (var httpClient = new HttpClient()) {
            var json = httpClient.GetStringAsync(SourceURI);
            return json.Result;
        }
    }

¿Me estoy perdiendo de algo?

Nota: por alguna razón, cuando interrumpo el método Async anterior, cuando llega a la línea "var json = aguarda httpClient.GetStringAsync (SourceURI)" simplemente se sale del punto de interrupción y no puedo volver al método.


public string RetrieveHolidayDatesFromSource() {
    var result = this.RetrieveHolidayDatesFromSourceAsync().Result;
    /** Do stuff **/
    var returnedResult  = this.TransformResults(result.Result); /** Where result gets used **/
    return returnedResult;
}

Si agrega .Resultado a la llamada asíncrona, se ejecutará y esperará a que llegue el resultado, lo que obliga a que sea síncrono

ACTUALIZAR:

private static string stringTest()
{
    return getStringAsync().Result;
}

private static async Task<string> getStringAsync()
{
    return await Task.FromResult<string>("Hello");
}
static void Main(string[] args)
{
    Console.WriteLine(stringTest());

}

Para abordar el comentario: Esto funciona sin problemas.





async-await