132

I've got an interface with some functions that return Task. Some of the classes that implement the interface do not have anything to await, while others might just throw - so the warnings are spurious and annoying.

Is it possible to suppress these warnings? E.g.:

public async Task<object> test()
{
    throw new NotImplementedException();
}

yields:

warning CS1998: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Ian Kemp
  • 28,293
  • 19
  • 112
  • 138
Simon
  • 2,449
  • 2
  • 18
  • 20
  • 1
    When not using the new await keyword in a function marked as async. – Simon Nov 06 '12 at 06:05
  • How about showing us a code sample that reproduces the problem? – John Saunders Nov 06 '12 at 06:12
  • 1
    see this: [Should I worry about “This async method lacks 'await' operators and will run synchronously” warning](https://stackoverflow.com/q/29923215/2803565) – S.Serpooshan Mar 09 '19 at 15:13
  • The use of `#pragma warning disable 1998` as per the answer below from [Jamie Marcia](https://stackoverflow.com/a/16067003/57335) specifically answers the actual question asked ... that being "Is it possible to suppress these warnings?" – Simon Sanderson Feb 15 '23 at 07:49

17 Answers17

136

I've got an interface with some async functions.

Methods returning Task, I believe. async is an implementation detail, so it can't be applied to interface methods.

Some of the classes that implements the interface does not have anything to await, and some might just throw.

In these cases, you can take advantage of the fact that async is an implementation detail.

If you have nothing to await, then you can just return Task.FromResult:

public Task<int> Success() // note: no "async"
{
  ... // non-awaiting code
  int result = ...;
  return Task.FromResult(result);
}

In the case of throwing NotImplementedException, the procedure is a bit more wordy:

public Task<int> Fail() // note: no "async"
{
  var tcs = new TaskCompletionSource<int>();
  tcs.SetException(new NotImplementedException());
  return tcs.Task;
}

If you have a lot of methods throwing NotImplementedException (which itself may indicate that some design-level refactoring would be good), then you could wrap up the wordiness into a helper class:

public static class TaskConstants<TResult>
{
  static TaskConstants()
  {
    var tcs = new TaskCompletionSource<TResult>();
    tcs.SetException(new NotImplementedException());
    NotImplemented = tcs.Task;
  }

  public static Task<TResult> NotImplemented { get; private set; }
}

public Task<int> Fail() // note: no "async"
{
  return TaskConstants<int>.NotImplemented;
}

The helper class also reduces garbage that the GC would otherwise have to collect, since each method with the same return type can share its Task and NotImplementedException objects.

I have several other "task constant" type examples in my AsyncEx library.

Lorenzo Polidori
  • 10,332
  • 10
  • 51
  • 60
Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 2
    I did not think of losing the keyword. As you say, async got nothing to do with the interface. My bad, thank you. – Simon Nov 06 '12 at 21:38
  • 3
    Can you recommend an approach where the return type is just Task (without a result?) – Mike May 12 '14 at 16:02
  • @Mike: Since `Task` inherits from `Task`, the same solutions (`Task.FromResult`, `TaskCompletionSource`) work just fine. There isn't an easy and efficient way to create just a plain `Task` like that, so it's best to create a `Task` and (implicitly) cast it to `Task`. – Stephen Cleary May 12 '14 at 17:25
  • 13
    **Warning:** This approach can cause problems because errors will not be propagated the way you expect. Normally the caller will expect an exception in your method to be surfaced within the `Task`. Instead, your method will throw before it even gets a chance to create a `Task`. I really think the best pattern is to define an `async` method with no `await` operators. This ensures the code within the method all gets treated as part of the `Task`. – Bob Meyers Jan 21 '16 at 03:38
  • 13
    To avoid CS1998, you can add `await Task.FromResult(0);` to your method. This should not have any significant perf impact (unlike Task.Yield()). – Bob Meyers Jan 21 '16 at 03:45
  • See this answer further down, it's much less complicated: http://stackoverflow.com/a/24915403/32238 – Andrew Theken Jun 24 '16 at 18:21
  • 4
    @AndrewTheken: These days you can just do `return Task.CompletedTask;` - the simplest of all. – Stephen Cleary Jun 24 '16 at 18:48
  • @StephenCleary For the "void" case, this is fine. However, if the interface requires Task and you have a "fast" implementation of it that doesn't need async, adding the `await Task.Yield();` is easier than wrapping everything up. – Andrew Theken Jun 24 '16 at 18:55
  • 1
    @AndrewTheken: I still think `Task.FromResult(result)` would be more idiomatic there. `Task.Yield` introduces an async point where there is no async work. – Stephen Cleary Jun 24 '16 at 19:21
  • 2
    You get cleaner code if you simply disable the CS1998 warning in your `.csproj`. – binki Oct 13 '17 at 01:58
  • @StephenCleary can you please take a look to my answer provided [here](https://stackoverflow.com/a/55078181/2803565) and give your comments? – S.Serpooshan Mar 09 '19 at 15:02
  • 1
    @BobMeyers I think your approach of using `await Task.FromResult(0);` to avoid CS1998 is valuable enough to be a separate answer here. – RonC Dec 23 '19 at 17:21
  • This innocent looking optimization is not semantically equal to the naive (and more cumbersome) approach, and this can cause problems in situations where the identity of `Task` objects matters. `Task.CompletedTask` has the same problem; usually (but not guaranteed) it is a singleton instance. – dialer May 19 '21 at 14:36
  • @dialer: `where the identity of Task objects matters` - I recommend avoiding this situation. Tasks can be cached in many, many situations. – Stephen Cleary May 19 '21 at 14:51
82

Another option, if you want to keep the body of the function simple and not write code to support it, is simply to suppress the warning with #pragma:

#pragma warning disable 1998
public async Task<object> Test()
{
    throw new NotImplementedException();
}
#pragma warning restore 1998

If this is common enough, you could put the disable statement at the top of the file and omit the restore.

http://msdn.microsoft.com/en-us/library/441722ys(v=vs.110).aspx

Jamie Macia
  • 1,129
  • 1
  • 9
  • 10
  • 1
    Or in project settings, Build, Errors and Warnings, Suppress specific warnings, add 1998. The way I see it, it's easier to write async everywhere rather than explicitly deal with the Task type everywhere, when only select implementations of an interface need await. – Alan Baljeu Jun 09 '22 at 13:33
46

Another way to preserve the async keyword (in case you want to keep it) is to use:

public async Task StartAsync()
{
    await Task.Yield();
}

Once you populate the method you can simply remove the statement. I use this a lot especially when a method might await something but not every implementation actually does.

Simon Mattes
  • 4,866
  • 2
  • 33
  • 53
  • This should be the accepted answer. Sometimes the interface implementations don't need to be async, this is much cleaner than wrapping everything in a `Task.Run` call. – Andrew Theken Jun 24 '16 at 18:18
  • 18
    await Task.CompletedTask; // might be a better option – Frode Nilsen Mar 09 '17 at 13:13
  • @FrodeNilsen for some reason `Task.CompletedTask` does not seem to exist anymore. – Sebastián Vansteenkiste Jul 19 '17 at 19:41
  • 2
    @SebastiánVansteenkiste .Net Framework 4.6->, UWP 1.0->, .Net Core 1.0-> – Frode Nilsen Jul 20 '17 at 10:14
  • 1
    @AndrewTheken It took me a while to reach the conclusion that this answer and your comment apply specifically to the case where the implementation is empty or just throws an exception (as in the original question). If an implementation does return a value, it seems like `Task.FromResult` is the better answer. For that matter, if you *are* going to throw an exception, it seems another answer has come into play regarding `Task.FromException` making this never the ideal solution. Would you agree? – BlueMonkMN Jan 15 '18 at 16:53
23

There is difference between solutions and strictly speaking you should know how caller is going to call the async method, but with default usage pattern that assumes ".Wait()" on method result - "return Task.CompletedTask" is the best solution.

    BenchmarkDotNet=v0.10.11, OS=Windows 10 Redstone 3 [1709, Fall Creators Update] (10.0.16299.192)
Processor=Intel Core i5-2500K CPU 3.30GHz (Sandy Bridge), ProcessorCount=4
Frequency=3233537 Hz, Resolution=309.2589 ns, Timer=TSC
.NET Core SDK=2.1.2
  [Host] : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT
  Clr    : .NET Framework 4.7 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.2600.0
  Core   : .NET Core 2.0.3 (Framework 4.6.25815.02), 64bit RyuJIT


         Method |  Job | Runtime |         Mean |       Error |      StdDev |       Median |          Min |          Max | Rank |  Gen 0 |  Gen 1 |  Gen 2 | Allocated |
--------------- |----- |-------- |-------------:|------------:|------------:|-------------:|-------------:|-------------:|-----:|-------:|-------:|-------:|----------:|
 CompletedAwait |  Clr |     Clr |    95.253 ns |   0.7491 ns |   0.6641 ns |    95.100 ns |    94.461 ns |    96.557 ns |    7 | 0.0075 |      - |      - |      24 B |
      Completed |  Clr |     Clr |    12.036 ns |   0.0659 ns |   0.0617 ns |    12.026 ns |    11.931 ns |    12.154 ns |    2 | 0.0076 |      - |      - |      24 B |
         Pragma |  Clr |     Clr |    87.868 ns |   0.3923 ns |   0.3670 ns |    87.789 ns |    87.336 ns |    88.683 ns |    6 | 0.0075 |      - |      - |      24 B |
     FromResult |  Clr |     Clr |   107.009 ns |   0.6671 ns |   0.6240 ns |   107.009 ns |   106.204 ns |   108.247 ns |    8 | 0.0584 |      - |      - |     184 B |
          Yield |  Clr |     Clr | 1,766.843 ns |  26.5216 ns |  24.8083 ns | 1,770.383 ns | 1,705.386 ns | 1,800.653 ns |    9 | 0.0877 | 0.0038 | 0.0019 |     320 B |
 CompletedAwait | Core |    Core |    37.201 ns |   0.1961 ns |   0.1739 ns |    37.227 ns |    36.970 ns |    37.559 ns |    4 | 0.0076 |      - |      - |      24 B |
      Completed | Core |    Core |     9.017 ns |   0.0690 ns |   0.0577 ns |     9.010 ns |     8.925 ns |     9.128 ns |    1 | 0.0076 |      - |      - |      24 B |
         Pragma | Core |    Core |    34.118 ns |   0.4576 ns |   0.4281 ns |    34.259 ns |    33.437 ns |    34.792 ns |    3 | 0.0076 |      - |      - |      24 B |
     FromResult | Core |    Core |    46.953 ns |   1.2728 ns |   1.1905 ns |    46.467 ns |    45.674 ns |    49.868 ns |    5 | 0.0533 |      - |      - |     168 B |
          Yield | Core |    Core | 2,480.980 ns | 199.4416 ns | 575.4347 ns | 2,291.978 ns | 1,810.644 ns | 4,085.196 ns |   10 | 0.0916 |      - |      - |     296 B |

Note: FromResult can't be directly compared.

Test Code:

   [RankColumn, MinColumn, MaxColumn, StdDevColumn, MedianColumn]
   [ClrJob, CoreJob]
   [HtmlExporter, MarkdownExporter]
   [MemoryDiagnoser]
 public class BenchmarkAsyncNotAwaitInterface
 {
string context = "text context";
[Benchmark]
public int CompletedAwait()
{
    var t = new CompletedAwaitTest();
    var a = t.DoAsync(context);
    a.Wait();
    return t.Length;
}

[Benchmark]
public int Completed()
{
    var t = new CompletedTest();
    var a = t.DoAsync(context);
    a.Wait();
    return t.Length;
}

[Benchmark]
public int Pragma()
{
    var t = new PragmaTest();
    var a = t.DoAsync(context);
    a.Wait();
    return t.Length;
}

[Benchmark]
public int Yield()
{
    var t = new YieldTest();
    var a = t.DoAsync(context);
    a.Wait();
    return t.Length;
}

    [Benchmark]
    public int FromResult()
    {
        var t = new FromResultTest();
        var t2 = t.DoAsync(context);
        return t2.Result;
    }

public interface ITestInterface
{
    int Length { get; }
    Task DoAsync(string context);
}

class CompletedAwaitTest : ITestInterface
{
    public int Length { get; private set; }
    public async Task DoAsync(string context)
    {
        Length = context.Length;
        await Task.CompletedTask;
    }
}

class CompletedTest : ITestInterface
{
    public int Length { get; private set; }
    public Task DoAsync(string context)
    {
        Length = context.Length;
        return Task.CompletedTask;
    }
}

class PragmaTest : ITestInterface
{
    public int Length { get; private set; }
    #pragma warning disable 1998
    public async Task DoAsync(string context)
    {
        Length = context.Length;
        return;
    }
    #pragma warning restore 1998
}

class YieldTest : ITestInterface
{
    public int Length { get; private set; }
    public async Task DoAsync(string context)
    {
        Length = context.Length;
        await Task.Yield();
    }
}

    public interface ITestInterface2
    {
        Task<int> DoAsync(string context);
    }

    class FromResultTest : ITestInterface2
    {
        public async Task<int> DoAsync(string context)
        {
            var i = context.Length;
            return await Task.FromResult(i);
        }
    }

}

Roman Pokrovskij
  • 9,449
  • 21
  • 87
  • 142
  • 1
    It is unfortunate that the `#pragma` one seems to incur overhead. Probably just as much overhead as if instead of returning `CompletedTask` you created and completed an `AsyncOperation`. It’d be nice to be able to tell the compiler that it’s OK to skip that when the method runs synchronously anyway. – binki Dec 16 '17 at 17:48
  • 1
    How similar do you suppose `Task.CompletedTask` is similar to `Task.FromResult`? It would be interesting to know - I expect FromResult would be most analogous and still the best performer if you have to return a value. – BlueMonkMN Jan 15 '18 at 16:59
  • I will add it. I think state machine code will be more verbose in this case and CompletedTask will win .Let see – Roman Pokrovskij Jan 15 '18 at 19:51
  • 1
    Would be nice seeing this updated for .NET Core 2.2, since [allocations in the async state-machines have been drastically improved](https://twitter.com/matthewwarren/status/1068556386630545408/) – Tseng Apr 15 '19 at 11:35
  • 1
    @Tseng I've run the benchmarks on .NET Core 2.2.0. Obviously, the total time is different due to different hardware, but the ratio remains roughly the same: Method | .NET Core 2.0.3 Mean | .NET Core 2.2.0 Mean Completed | 100% | 100% CompletedAwait | 412.57% | 377.22% FromResult | 520.72% | 590.89% Pragma | 378.37% | 346.64% Yield | 27514.47% | 23602.38% – Storm Jul 02 '19 at 06:42
10

I know this is an old thread, and perhaps this won't have the right effect for all usages, but the following is as close as I can get to being able to simply throw a NotImplementedException when I haven't yet implemented a method, without altering the method signature. If it's problematic I'd be happy to know about it, but it barely matters to me: I only use this while in development anyway, so how it performs isn't all that important. Still, I'd be happy to hear about why it's a bad idea, if it is.

public async Task<object> test()
{
    throw await new AwaitableNotImplementedException<object>();
}

Here's the type I added to make that possible.

public class AwaitableNotImplementedException<TResult> : NotImplementedException
{
    public AwaitableNotImplementedException() { }

    public AwaitableNotImplementedException(string message) : base(message) { }

    // This method makes the constructor awaitable.
    public TaskAwaiter<AwaitableNotImplementedException<TResult>> GetAwaiter()
    {
        throw this;
    }
}
rrreee
  • 753
  • 1
  • 6
  • 20
9

Just as an update to Stephen's Answer, you no longer need to write the TaskConstants class as there is a new helper method:

    public Task ThrowException()
    {
        try
        {
            throw new NotImplementedException();
        }
        catch (Exception e)
        {
            return Task.FromException(e);
        }
    }
Matt
  • 1,446
  • 19
  • 17
  • 4
    Don’t do this. The stack trace will not point at your code. Exceptions must be thrown to be completely initialized. – Daniel B Aug 19 '19 at 15:09
  • 1
    Daniel B - Yes, you are absolutely right. I have modified my answer to correctly throw the exception. – Matt Nov 09 '19 at 11:23
9

You might try this:

public async Task<object> test()
{
await Task.CompletedTask; 
}
Faisal Mehboob
  • 609
  • 7
  • 17
3

In case you already link against Reactive Extension, you can also do:

public async Task<object> NotImplemented()
{
    await Observable.Throw(new NotImplementedException(), null as object).ToTask();
}

public async Task<object> SimpleResult()
{
    await Observable.Return(myvalue).ToTask();
}

Reactive and async/await are both amazing in and by themselves, but they also play well together.

Includes needed are:

using System.Reactive.Linq;
using System.Reactive.Threading.Tasks;
Lorenzo Polidori
  • 10,332
  • 10
  • 51
  • 60
John
  • 6,693
  • 3
  • 51
  • 90
2

Try this:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS1998:Await.Warning")]

See: https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.codeanalysis.suppressmessageattribute?view=netframework-4.7.2

Paul Schroeder
  • 1,460
  • 1
  • 14
  • 21
  • This doesn't work for me. [CS1998 is not suppressed by SuppressMessage when building with MSBuild.exe](https://github.com/dotnet/roslyn/issues/28479) – Theodor Zoulias Feb 17 '22 at 19:40
2

Configure/Suppress it globally:

in .editorconfig

# CS1998: Async method lacks 'await' operators and will run synchronously
dotnet_diagnostic.CS1998.severity = suggestion

In a common, not high performance, application the overhead of unneccessary async is negligible, and the benefits of brain-off async-all-the-way for regular coders is more important. (+ additional compiler checks etc.)

yannik
  • 87
  • 4
  • "*the overhead of unnecessary async is negligible*". This is true, but how is it related with the rest of your answer? Suppressing the CS1998 warning is not about accepting a negligible overhead, it is about allowing potentially harmful bugs to pass unnoticed. – Theodor Zoulias Jul 30 '21 at 09:12
  • huh, which bugs could possibly being introcuded by running an async method synchronously? – yannik Aug 05 '21 at 12:55
  • yannik if my `async` method lacks `await`, it is quite possible that I forgot to `await` something that should be awaited. – Theodor Zoulias Aug 05 '21 at 13:08
  • 1
    no, that's a different warning: # CS4014: Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call. – yannik Aug 10 '21 at 11:28
  • Assuming that `var result = ProcessAsync();` is a bug, and `var result = await ProcessAsync();` is correct, the CS1998 warning may help me detect the bug. The CS4014 warning will not help me because it won't be triggered. This is only triggered with code like this: `ProcessAsync();`. – Theodor Zoulias Aug 10 '21 at 11:41
  • what do you do with the result? Your statement alone makes no sense: var result = ProcessAsync(); is type of Task. var result = await ProcessAsync(); is type of T. Additionaly, if you declare a result and dont use it, thats another Warning... fiddle it; I don't see any valid example – yannik Aug 23 '21 at 18:19
  • btw. u got the warning wrong. #CS1998 is about the Method you are in, not about the async methods you are calling. Its sufficient for the method you are in, if you have 1x await. But you can call 10x other async methods with or without await, none of interest for #CS1998. – yannik Aug 24 '21 at 13:21
  • Note to self: I added [*.{cs,razor,cshtml}] on top of the .editorconfig file to reduce the severity in my Blazor projects. Usually, reducing this severity is done by clicking the little lightbulb you get when you hover over the green squiggly line, but you won't get a lightbulb in razor/cshtml files in Blazor projects, so you have to do it manually – Linus Proxy Sep 08 '21 at 14:49
  • Today I learned that hiding errors and warnings from being shown is considered a solution. – Enrico Jan 18 '23 at 11:04
0

It might be occured cs1998 below.

public async Task<object> Foo()
{
    return object;
}

Then you can reform below.

public async Task<object> Foo()
{
    var result = await Task.Run(() =>
    {
        return object;
    });
    return result;
}
瀧谷賢司
  • 105
  • 8
0

If you don't have anything to await then return Task.FromResult

public Task<int> Success() // note: no "async"
{
  ... // Do not have await code
  var result = ...;
  return Task.FromResult(result);
}
priyanka
  • 159
  • 1
  • 3
0

Here is some alternatives depending on your method signature.

    public async Task Test1()
    {
        await Task.CompletedTask;
    }

    public async Task<object> Test2()
    {
        return await Task.FromResult<object>(null);
    }

    public async Task<object> Test3()
    {
        return await Task.FromException<object>(new NotImplementedException());
    }
Ludde
  • 527
  • 1
  • 6
  • 12
-1

Cleanest wait to suppress this warning is to just use await Task.CompletedTask; before throwing. This functions as a no-op

public async Task SomeMethod()
{
    await Task.CompletedTask;
    throw new NotImplementedException();
}
DLeh
  • 23,806
  • 16
  • 84
  • 128
-1

Use throw of yielded exception

public async Task<object> DoSomethingAsync()
{
    throw await Task.FromException<NotImplementedException>(new NotImplementedException());
}
AnGG
  • 679
  • 3
  • 9
-3
// This is to get rid of warning CS1998, please remove when implementing this method.
await new Task(() => { }).ConfigureAwait(false);
throw new NotImplementedException();
bkoelman
  • 11
  • 2
-4

You can drop the async keyword from the method and just have it return Task;

    public async Task DoTask()
    {
        State = TaskStates.InProgress;
        await RunTimer();
    }

    public Task RunTimer()
    {
        return new Task(new Action(() =>
        {
            using (var t = new time.Timer(RequiredTime.Milliseconds))
            {
                t.Elapsed += ((x, y) => State = TaskStates.Completed);
                t.Start();
            }
        }));
    }
Rakz
  • 1
  • 2