Search code examples
c#async-awaitsignalrazure-functionssignalr-service

How to call async methods from a azure function connected to a singnalr service why IAsyncCollector is used for return


I have a azure function connected to a singnalr service that why IAsyncCollector paramter is used to add the return via AddAsync. Now I have to call a async method to calculate the result but so I need to change the method signature to async but then I also have to change the result to Task what is not working at runtime.

[FunctionName("Join")]
public static async Task<Task> Join(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post")] JoinRequest message,
            [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
{
    await MyHelper.Instance.FillAsync(message);           
    return signalRMessages.AddAsync(
        new SignalRMessage
                {
                    Target = "joined",
                    Arguments = new[] { message }
                });
}

I read about how IAsyncCollector collector work, so awaitig AddAsync make no sense. But how can I call that async method or make IAsyncCollector work in an async method. I not really found some sample how this may work. There are some samples in ms doc but they have no return or do not use async.


Solution

  • AddAsync is a Task returning method. Since your function is already async, you should be awaiting the value.

    The way your function is currently written you are returning a Task<Task>. The azure function runtime will wait for the outer task, but will basically discard the inner one. That outer task completes practically instantly since it's only work is to create another task. This means that the Task returned from the AddAsync operation will be discarded and will likely have not completed before the infrastructure calls Flush on the collector. This will therefore result in the loss of the collected value.

    I'm not sure why you are under the impression that you shouldn't await the method. I suspect you ended up with the Task<Task> return type because of this assumption*.

    The correct way to implement this is to have your function return a Task and to await the AddAsync call:

    // returns Task not Task<Task>
    [FunctionName("Join")]
    public static async Task Join(
                [HttpTrigger(AuthorizationLevel.Anonymous, "post")] JoinRequest message,
                [SignalR(HubName = "chat")] IAsyncCollector<SignalRMessage> signalRMessages)
    {
        await MyHelper.Instance.FillAsync(message)
        // no return, await the call       
        await signalRMessages.AddAsync(
            new SignalRMessage
                    {
                        Target = "joined",
                        Arguments = new[] { message }
                    });
    }
    

    * As opposed to you starting out with the return type of Task<Task> already selected and determining the only way to make that happen was to return the Task from AddAsync without awaiting it.