Async / await in .Net & # 8594; Calling a service method that has no asynchronous implementation

Basically, I need to make a remote request using the .Net SDK vendor's SDK to get some information. Their SDK doesn't have asynchronous implementations on their methods, so I'm trying to come up with something myself. I basically want this request to be sent to a synchronous method, and only wait for it for a while. If the request is taking too long, I need to take action and report it to the client in our web application.

I am wondering if this is the best way to do this, or is there a better way? The code below is a service method called from a controller action.

    public async Task<bool> SignersAdded(string packageId)
    {
        var timeout = 5000;

        var task = Task.Run(() =>
        {
            var package = _eslClient.GetPackage(new PackageId(packageId));
            return package != null && package.Documents.Values.Any(x => x.Signatures.Any());
        });

        var stopwatch = Stopwatch.StartNew();

        while (!task.IsCompleted)
        {
            if (stopwatch.ElapsedMilliseconds >= timeout)
                return false;
        }

        return false;
    }

      

+3


source to share


4 answers


Task.Wait

has an overload that takes on a timeout value int

.



public Task<bool> SignersAdded(string packageId)
{
    var timeout = 5000;

    var task = Task.Run(() =>
    {
        var package = _eslClient.GetPackage(new PackageId(packageId));
        return package != null && package.Documents.Values.Any(x => x.Signatures.Any());
    });

    if(!task.Wait(1000 /*timeout*/))
    {
        // timeout
        return false;
    }
    return task.Result;
}

      

0


source


Your method doesn't matter await

, so it runs synchronously. In addition, your loop while

will spin the processor, blocking the calling code until the task completes.

The best approach might be the following:

var task = Task.Run(/* your lambda */)
var finishedTask = await Task.WhenAny(Task.Delay(timeout), task);
return finishedTask == task;

      



Thus, we create a separate delay task during this time, and we await the completion of the first task. This will run truly asynchronously - there is no loop while

that will write processor cycles.

(The above assumes the timeout is milliseconds. If not, use an overload for Delay

instead of an argument TimeSpan

.)

0


source


You're right: run a task that calls GetPackage. After that, you can continue to do other things.

After a while, when you need the result, you can wait for the task to complete. However, you don't need to do Task.Wait. It is much easier to use async / await.

To do this, you need to do three things:

  • Declare your async function
  • Instead of void return Task and instead of type TResult return Task <TResult

    >. You have already done this.
  • Instead of waiting for the task to complete, use wait

Your function will look much simpler:

public **async** Task<bool> SignersAdded(string packageId)
{
   var timeout = TimeSpan.FromSeconds(5);
   var task = Task.Run(() =>
   {
      var package = _eslClient.GetPackage(new PackageId(packageId));
      return package != null
          && package.Documents.Values
                    .Any(x => x.Signatures.Any());
    });

    // if desired you can do other things here
    // once you need the answer start waiting for it and return the result:
    return await Task;
}

      

if you have a function that returns TResult, the asynchronous version returns Task <TResult

>.

wait return value Task <TResult

> - TResult

However, if you want to be able to wait with a timeout, you can do the following:

var tokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(1);
// cancel after 1 second
try
{
    return await task.Run( () => ..., tokenSource.Token);
}
catch (OperationCanceledException exc)
{
    // handle timeout
}
finally
{
    // do necessary cleanup
}

      

The downside to your async function is that all callers must be asynchronous as well, and all must return Task or Task <TResult

>. There is one exception:

The event handler can be asynchronous, but it can return void

Example:

private async void OnButton1_clicked(object sender,   )

      

0


source


Take a look at the TaskCompletionSource object and the CancellationToken class. Samples here: Timeout for an Asynchronization Method Implemented with a TaskCompletionSource or How to Cancel a TaskCompletion Source Using a Timeout

0


source







All Articles