Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for Async function before returning

I am creating a messaging system and I am facing a problem. I need to Publish a message and wait for the response before returning the Publish function.

This is what my functions look like

public async Task<bool> Publish(int ClientId, string msg){
    ...
    // Wait and check if the client the message was sent to respond
    // if that does not happen within 5 seconds, return false, else true
}

private async Task MessageIntercept(int ClientId, string msg){
    // Intercepts all messages
    ...
}

Both of these functions are on the server and the MessageIntercept task is automatically run whenever a message is sent (including the one sent with the Publish method). I can send a message from my asp.net website project by calling on the server project's Publish function mentioned above

Basically what i want to be able to do is call bool Success = Publish(1,"This is a test") and be able to determine whether or not the message was successfully sent, that the client understood and revived the message within 5 seconds.

This is what happens step by step:

  1. I send a message from the server to the device with Publish
  2. The message is intercepted by the MessageIntercept method (which I do not really care about, but the code is written so that all messages are intercepted)
  3. The client receives and handles the message
  4. The client responds, and the message is intercepted in MessageIntercept which is where I would like to verify the message before returning the Publish method

Example Message;

Server Message:

{
    ClientId: 13,
    msg: "Hello World"
}

Client Response:

{
    ClientId: 13,
    msg: "{Success: true}"
}

MessageIntercept intercepts all messages, including the request just send which should be ignored due to it beeing a request not a response. However once the client responds with a message would I like to tell the Publish method that the response has been successfully completed and then return true. Else if the client does not respond within 5 seconds it should presume false.

like image 294
Steinar Avatar asked Dec 18 '25 09:12

Steinar


1 Answers

You can use a helper method such as this:

public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
{
    return await Task.WhenAny(task, Task.Delay(timeout)) == task;
}

Sample usage:

using System;
using System.Threading.Tasks;

namespace Demo
{
    public class Program
    {
        public static async Task Main()
        {
            if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(1)))
                Console.WriteLine("First await completed");
            else
                Console.WriteLine("First await didn't complete");

            if (await WaitFor(MyAsyncMethod(), TimeSpan.FromSeconds(3)))
                Console.WriteLine("Second await completed");
            else
                Console.WriteLine("Second await didn't complete");
        }

        public static async Task MyAsyncMethod()
        {
            await Task.Delay(2000);
        }

        public static async Task<bool> WaitFor(Task task, TimeSpan timeout)
        {
            return await Task.WhenAny(task, Task.Delay(timeout)) == task;
        }
    }
}

For your Publish() method, the call may look like this:

if (await WaitFor(Publish(1, "msg"), TimeSpan.FromSeconds(5)))
    ...

However, be aware that the disadvantage of using this approach is that if the timeout is exceeded, then any exceptions thrown by the task will not be observed.

If you need to handle any exceptions that may occur after you've given up waiting for the task, you could pass an exception-handling delegate like so:

public static async Task<bool> WaitFor(Task task, TimeSpan timeout, Action<Exception> handleException)
{
    var wrapperTask = task.ContinueWith(
        t => handleException(t.Exception.InnerException), 
        TaskContinuationOptions.OnlyOnFaulted);

    return await Task.WhenAny(wrapperTask, Task.Delay(timeout)) == task;
}

Then you could call it like this:

public static async Task Main()
{
    if (await WaitFor(
        MyAsyncMethod(), 
        TimeSpan.FromSeconds(1),
        exception => Console.WriteLine("Exception: " + exception.Message))
    )
        Console.WriteLine("First await completed");
    else
        Console.WriteLine("First await didn't complete");

    Console.ReadLine();
}
like image 188
Matthew Watson Avatar answered Dec 19 '25 23:12

Matthew Watson



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!