What is `ValueTask` and when should you use it over `Task`?

4 minadvanced.NETValueTaskperformanceasync-await

Quick Answer

`ValueTask`/`ValueTask<T>` is a struct-based alternative to `Task` that avoids a heap allocation when an async method frequently completes synchronously (e.g., cached results). It reduces GC pressure in hot paths but has strict rules: don't await it more than once, don't block on it, and don't use it concurrently. Use `Task` by default and `ValueTask` only for performance-critical APIs that often complete synchronously.

Detailed Answer

ValueTask is a value type (struct) that represents an asynchronous operation, introduced to reduce heap allocations in high-performance scenarios.

Key differences from Task:

Task:

  • Reference type (class) - allocated on the heap
  • Can be awaited multiple times
  • Can be cached and reused safely
  • Slightly more overhead due to allocation

ValueTask:

  • Value type (struct) - can be allocated on the stack
  • Should only be awaited once
  • More efficient when the operation completes synchronously
  • Less garbage collection pressure

When to use ValueTask:

Use ValueTask when:

  1. The operation frequently completes synchronously (e.g., cached results)
  2. Performance is critical and you want to avoid allocations
  3. You're building high-throughput libraries or APIs
  4. The result is only awaited once
// Good use case: Cache-backed operation
public async ValueTask GetUserAsync(int userId)
{
    // Check cache first - often completes synchronously
    if (_cache.TryGetValue(userId, out var user))
    {
        return user; // No Task allocation needed
    }
    
    // Cache miss - fetch from database
    user = await _database.GetUserAsync(userId);
    _cache.Add(userId, user);
    return user;
}

When to use Task:

Use Task when:

  1. The operation is always asynchronous
  2. You need to await the result multiple times
  3. You need to store the task for later use
  4. Working with public APIs where simplicity matters
// Task is better here - result may be awaited multiple times
public Task FetchDataAsync()
{
    return _httpClient.GetStringAsync("https://api.example.com/data");
}

Important rules for ValueTask:

  • Only await a ValueTask once
  • Don't store ValueTask in fields or properties
  • Convert to Task if you need multiple awaits: valueTask.AsTask()