SubscribeToTickerAsync
is going to be invoked more than once, which makes me believe ValueTask is the better choice here. Otherwise, the impact of avoiding a single allocation of a tiny object is practically zero.
What stops me from replacing Task
-> ValueTask
is the following statement. Does that mean that if there are multiple awaits it's a bad idea to return ValueTask?
ValueTask can only be awaited once, and not to be awaited concurrently. This means that each ValueTask can be consumed only once.
Here the word “consume” implies that a ValueTask can asynchronously wait for (await) the operation to complete or take advantage of AsTask to convert a ValueTask to a Task. However, a ValueTask should be consumed only once, after which the ValueTask should be ignored.
_requestManager.GetResponseAsync
is a TaskCompletionSource<T, Y>
implementation.
_websocketClient.SendAsync
is technically System.Threading.Channels
implementation.
public ValueTask SendAsync(Message message)
{
return _sendChannel.Writer.WriteAsync(message);
}
Code
private async Task SendAsync(SocketRequest request)
{
await _throttlingLimiter.WaitAsync().ConfigureAwait(false);
var json = JsonSerializer.Serialize(request);
var message = new Message(Encoding.UTF8.GetBytes(json));
await _websocketClient.SendAsync(message).ConfigureAwait(false);
var responseTask = await _requestManager.GetResponseAsync(request.Id, TaskCreationOptions.RunContinuationsAsynchronously, CancellationToken.None).ConfigureAwait(false);
// TODO: Process that result and return it
_logger.LogInformation("Message: {Message}", Encoding.UTF8.GetString(responseTask.Span));
}
public async Task SubscribeToTickerAsync(Func<BookPrice, ValueTask> handler)
{
ArgumentNullException.ThrowIfNull(handler);
var request = new SocketRequest("SUBSCRIBE", new[] { "btcusdt@bookTicker" }, GetNextRequestId());
await SendAsync(request).ConfigureAwait(false);
if (!_subscriptionManager.AddSubscription(new Subscription<SocketRequest, Notification>("btcusdt@bookTicker", request, n => handler(n.Data.Deserialize<BookPrice>()!))))
{
_logger.LogDebug("Could not subscribe");
}
_logger.LogDebug("Successfully subscribed");
}
public async Task UnsubscribeTickerAsync()
{
var request = new SocketRequest("UNSUBSCRIBE", new[] { "btcusdt@bookTicker" }, GetNextRequestId());
await SendAsync(request).ConfigureAwait(false);
if (!_subscriptionManager.RemoveSubscription("btcusdt@bookTicker"))
{
_logger.LogDebug("Could not unsubscribe");
}
_logger.LogDebug("Successfully unsubscribed");
}
Does that Task.Run
count as multiple await?
private void OnConnected(object? sender, EventArgs e)
{
_logger.LogTrace("Connected");
_keepAliveTimer?.Start();
foreach (var subscription in _subscriptionManager.GetSubscriptions())
{
Task.Run(async () =>
{
await SendAsync(subscription.Request).ConfigureAwait(false);
});
}
}