-2

I have an async method inside BackgroundService where I need to wait until a specific condition is met within a delegate that can be called multiple times. Here's the code snippet:

private async Task BuyAndSetLimitSellOrder(string pair, CancellationToken cancellationToken)
{
    var buyOrder = await BuyAndWriteOrderToDb(pair);

    var sellOrder = await SetSellAndWriteOrderToDb(pair, buyOrder);

    var handleReceivedMessageToken = new CancellationTokenSource();

    var pingDelegate = await _binanceService.SubscribeToUserData(messageHandler: async userData =>
    {
        await HandleReceivedMessage(userData, sellOrder.Id, handleReceivedMessageToken);
    }, cancellationToken);

    // Wait here until a specific string is received within the HandleReceivedMessage delegate.
}

In the above code i want to wait until a specific string is received within the HandleReceivedMessage delegate, which can be called multiple times by a WebSocket.

Here is SubscribeToUserData code:

    public async Task<Func<Task>> SubscribeToUserData(Func<string, Task> messageHandler, CancellationToken cancellationToken)
    {
        string response = await _userDataStreams.CreateSpotListenKey();
        string listenKey = (System.Text.Json.JsonSerializer.Deserialize<CreateSpotListenKeyResponse>(response) ?? throw new FormatException("Invalid AccountInformation string")).ListenKey;
        UserDataWebSocket websocket = new(listenKey, _configuration.BinanceWssBaseUrl);
        websocket.OnMessageReceived(messageHandler, CancellationToken.None);
        await websocket.ConnectAsync(CancellationToken.None);

        return () => PingSpotListenKey(listenKey);
    }

I tried multiple things including ManualResetEvent and while loop waiting for canncelation token but anything i put there keep blocking call to HandleReceivedMessage. Also im using binance-connector-dotnet for binance API calls

exzzy
  • 19
  • 4
  • Are you looking to do something like this? https://stackoverflow.com/questions/50123585/awaiting-a-callback-method – gunr2171 May 15 '23 at 18:39
  • yes and there is actually example of that in library examples here, but it just doesn't work for me. It keeps waiting on TaskCompletionSoruce.Task and delegate is never called. https://github.com/binance/binance-connector-dotnet/blob/master/Examples/CSharp/WebSocketStream/UserDataWebSocket_Example.cs – exzzy May 15 '23 at 22:46
  • Did you get any errors when debugging? Will `HandleReceivedMessage` be called if synchronous? – Chen May 16 '23 at 09:07
  • No, there are no errors. I don't understand what you mean by "if synchronous" because the delegate is of type `Func`, so it has to be a `Task`. However, `HandleReceivedMessage` will always be called if there is no code after the `SubscribeToUserData` method call. – exzzy May 16 '23 at 16:45
  • just put Wait() to wait until task completed. please, check this article. https://stackoverflow.com/questions/15149811/how-to-wait-for-async-method-to-complete – Power Mouse May 17 '23 at 19:08

1 Answers1

0

Not sure if you can change the signature of the HandleReceivedMessage delegate, but here is a general pattern for using TaskCompletionSource<>.

If the Delegate is defined in 3rd party code maybe you can put the TaskCompletionSource in the userData.

Inside the HandleReceivedMessage delegate body call tcs.SetResult(true) when you meet your condition.

private async Task BuyAndSetLimitSellOrder(string pair, CancellationToken cancellationToken)
{
    var tcs = new TaskCompletionSource<bool>();

    var buyOrder = await BuyAndWriteOrderToDb(pair);

    var sellOrder = await SetSellAndWriteOrderToDb(pair, buyOrder);

    var handleReceivedMessageToken = new CancellationTokenSource();

    var pingDelegate = await _binanceService.SubscribeToUserData(messageHandler: async userData =>
    {
        await HandleReceivedMessage(userData, sellOrder.Id, 
 tcs, handleReceivedMessageToken);
    }, cancellationToken);

    // Wait here until a specific string is received within the HandleReceivedMessage delegate.

  _ = await tcs.Task;
}
Dustin S.
  • 46
  • 5
  • Yes i can, but here is the same problem as the other solutions i tried, the program stays at the line await tcs.Task; waiting for tcs.SetResult(true); in HandleReceivedMessage but HandleReceivedMessage is never called. But when i remove await tcs.Task; HandleReceivedMessage is normally called. – exzzy May 15 '23 at 21:28