All Projects → awaescher → ObviousAwait

awaescher / ObviousAwait

Licence: MIT license
🧵 Expressive aliases to ConfigureAwait(true) and ConfigureAwait(false)

Programming Languages

C#
18002 projects
powershell
5483 projects
shell
77523 projects

Projects that are alternatives of or similar to ObviousAwait

Concurrencpp
Modern concurrency for C++. Tasks, executors, timers and C++20 coroutines to rule them all
Stars: ✭ 340 (+518.18%)
Mutual labels:  tasks, multithreading, threading, await
awesome-dotnet-async
A curated list of awesome articles and resources to learning and practicing about async, threading, and channels in .Net platform. 😉
Stars: ✭ 84 (+52.73%)
Mutual labels:  thread, threading, await
Thread
type safe multi-threading made easier
Stars: ✭ 34 (-38.18%)
Mutual labels:  thread, multithreading, threading
Swiftcoroutine
Swift coroutines for iOS, macOS and Linux.
Stars: ✭ 690 (+1154.55%)
Mutual labels:  thread, multithreading, await
Microjob
A tiny wrapper for turning Node.js worker threads into easy-to-use routines for heavy CPU loads.
Stars: ✭ 1,985 (+3509.09%)
Mutual labels:  thread, multithreading, threading
Asyncawaitbestpractices
Extensions for System.Threading.Tasks.Task and System.Threading.Tasks.ValueTask
Stars: ✭ 693 (+1160%)
Mutual labels:  task, threading, await
React Native Multithreading
🧵 Fast and easy multithreading for React Native using JSI
Stars: ✭ 164 (+198.18%)
Mutual labels:  thread, multithreading, threading
ProtoPromise
Robust and efficient library for management of asynchronous operations in C#/.Net.
Stars: ✭ 20 (-63.64%)
Mutual labels:  task, tasks, await
Unitask
Provides an efficient allocation free async/await integration for Unity.
Stars: ✭ 2,547 (+4530.91%)
Mutual labels:  task, thread
Kommander Ios
A lightweight, pure-Swift library for manage the task execution in different threads. Through the definition a simple but powerful concept, Kommand.
Stars: ✭ 167 (+203.64%)
Mutual labels:  task, thread
Asyncex
A helper library for async/await.
Stars: ✭ 2,794 (+4980%)
Mutual labels:  task, await
Swimmer
🏊 Swimmer - An async task pooling and throttling utility for JS
Stars: ✭ 94 (+70.91%)
Mutual labels:  task, await
Ai Platform
An open-source platform for automating tasks using machine learning models
Stars: ✭ 61 (+10.91%)
Mutual labels:  task, tasks
Taskbuilder.fs
F# computation expression builder for System.Threading.Tasks
Stars: ✭ 217 (+294.55%)
Mutual labels:  task, await
Taskmanager
A simple、 light(only two file)、fast 、powerful 、easy to use 、easy to extend 、 Android Library To Manager your AsyncTask/Thread/CallBack Jobqueue ! 一个超级简单,易用,轻量级,快速的异步任务管理器,类似于AsyncTask,但是比AsyncTask更好用,更易控制,从此不再写Thread ! ^_^
Stars: ✭ 25 (-54.55%)
Mutual labels:  task, thread
csharp-workshop
NDC London 2019, Workshop: Become a better C# programmer: more Value, more Expressions, no Waiting
Stars: ✭ 21 (-61.82%)
Mutual labels:  task, await
Android Next
Android Next 公共组件库
Stars: ✭ 768 (+1296.36%)
Mutual labels:  task, threading
composer
API-first task runner with three methods: task, run and watch.
Stars: ✭ 35 (-36.36%)
Mutual labels:  task, tasks
YACLib
Yet Another Concurrency Library
Stars: ✭ 193 (+250.91%)
Mutual labels:  task, thread
ComposableAsync
Create, compose and inject asynchronous behaviors in .Net Framework and .Net Core.
Stars: ✭ 28 (-49.09%)
Mutual labels:  task, thread

If you ever had the joy of async programming in .NET, you might have seen this guy:

await anyTask.ConfigureAwait(false);

ConfigureAwait() tells the .NET runtime how you want your code to continue after it waited for the completion of an instruction.

This sounds easy so you might be surprised how many blog posts you'll find about this topic on the internet.

The most important one you should know is this one from Stephen Toub:

If you don't want to fight through this massive blog post, I highly recommend watching the following video. It's not mine but I wish every .NET developer would get a chance to watch it:

So why should I care?

If you are using async and await in your code without looking after ConfigureAwait(), you are very likely to either run into cross-thread exceptions and deadlocks or miss an opportunity to speed up async code.

To clarify, let's take two passages from Stephen's blog post:

Why would I want to use ConfigureAwait(false)?

ConfigureAwait(continueOnCapturedContext: false) is used to avoid forcing the callback to be invoked on the original context or scheduler. This has a few benefits: [...] Improving performance & Avoiding deadlocks.

Why would I want to use ConfigureAwait(true)?

You wouldn’t, unless you were using it purely as an indication that you were purposefully not using ConfigureAwait(false). [...] The ConfigureAwait() method accepts a Boolean because there are some niche situations in which you want to pass in a variable to control the configuration. But the 99% use case is with a hardcoded false argument value, ConfigureAwait(false).

The takeaway

  • Use ConfigureAwait(false) to tell the runtime that you don't need to run on the same context as you were called from.
  • If you don't use ConfigureAwait(false) you basically say the opposite ConfigureAwait(true) which tells the runtime that you want to proceed on the same context as you were called from. This makes it the default behavior.

What's the problem with ConfigureAwait()?

1. The term "configure await" does not tell anything about the intent of these methods.

"Configure await" does configure the way we want the awaited code to be continued. So this is not wrong at all. But, what are the options we have? true and false are not what you would use in natural language, like if you'd tell a co-worker, for example.

It's just like you hopefully will never see a method that configures file access like this:

File.ConfigureAccess("import.csv", isWriteOperation: true);

But instead one of these:

File.OpenRead("import.csv");
File.OpenWrite("import.csv");

OpenRead() and OpenWrite() both configure the way the file is accessed, but it's less that we "configure" anything. It's more what we want to do with it: the intent.

2. Passing boolean arguments to methods is considered to be a bad practice.

Passing boolean arguments is very known to developers. In many cases this is introduced to existing code when requirements arise, but I dare to say this is nothing to be proud of.

Having code that prints an invoice, for example, might get an additional boolean argument once the customer wants to include draft watermarks:

public void PrintInvoice(int id, bool isDraft)

The problem with this is that the caller has no chance to know what the argument means unless he uses a named argument (this is basically the same for other arguments like integers or strings but in most cases they deliver more context).

PrintInvoice(4711, true);          // what does 'true' mean here?
PrintInvoice(4711, isDraft: true); // ah, okay ...

Martin Fowler calls this the "FlagArgument anti-pattern".

So quick: What does that true argument mean again? How should this continue?

public async Task AddUserAsync(int id)
{
    var user = await _userService.GetAsync(id).ConfigureAwait(true);
    Users.Add(user);
}

This makes you ponder immediately, doesn't it? It shouldn't.

3. Skipping ConfigureAwait(true) hides the purpose.

Well, imagine you find this code:

public async Task AddUserAsync(int id)
{
    var user = await _userService.GetAsync(id);
    Users.Add(user);
}

Did the author of these lines just forget to add ConfigureAwait(false) or was it on purpose? If he ran that in a Windows Forms app, he might have had cross-thread exceptions with ConfigureAwait(false) because unless the first line, the second line does not run on the UI thread anymore. So maybe he wanted the continuation to happen on the captured context. We simply don't know.

If he added ConfigureAwait(true) explicitly, we could be assured that the author did that on purpose.

public async Task AddUserAsync(int id)
{
    var user = await _userService.GetAsync(id).ConfigureAwait(true);
    Users.Add(user);
}

However, chances are that he was not really sure which one to take:

true or false?

❇️ Introducing ObviousAwait

All of this above really bothered me for years. That's why I wanted to use obvious methods to define the behavior of code continuation. Methods which are ...

  • Simple but expressive
  • Easy to read but even easier to distinguish
  • As short as possible
  • Optional: Same length

I spent more time that I want to admit chosing the following two methods names. But I'm no native speaker, so you might find better ones. Please let me know.

Here they are ...

KeepContext() a.k.a. ConfigureAwait(true)

This method is called at the end of an awaitable instruction and tells us that we want to keep the context that instruction was called with so we still have it afterwards and we can use it to execute the code that follows.

public async Task AddUserAsync(int id)
{
    var user = await _userService.GetAsync(id).KeepContext();
    Users.Add(user);
}

It's clear and precise. The author clearly had an intent as he added this.

FreeContext() a.k.a. ConfigureAwait(false)

This method was harder to name: It's not just that we don't care that the current context is kept to execute the code that follows. We even tell the runtime that it basically can take whatever context it has to do so. By calling FreeContext() we can fasten things up by freeing the runtime from the responsibility to wait for a certain context.

public async Task AddUserAsync(int id)
{
    var user = await _userService.GetAsync(id).FreeContext();
    Users.Add(user);
}

The developer showed the intent that he does not need the current context to be kept. "Free context" sounds like a positive thing, something that you want to do if you can. Which you can ... in about 99% of the cases if we believe Stephen Toub.

I tried alternatives, like EmitContext(), IgnoreContext() or ElideContext() and much more but in my non-native-speaker opinion, they might be clearer about the fact that the current context can be ignored but they don't deliver the positive aspect that "freeing up" the choice of context might be a good thing.

Using ObviousAwait

In fact, these two methods just call ConfigureAwait(true) or ConfigureAwait(false) internally making this the smallest package I have ever built and very easy to use:

  • Add the NuGet package ObviousAwait to your project(s)
  • Replace ConfigureAwait(true) with KeepContext()
  • Replace ConfigureAwait(false) with FreeContext()
  • Enjoy

Another dependency just for this?

Yes, you're right. You don't need another dependency for aliasing ConfigureAwait(). We're on .NET and this is not npm (providing some really ridiculous packages). But please, if you like the idea of aliasing but you don't want to have another package dependency, just head over to that single code file and copy it over to your projects.

Alternatively, you can use dotnet-file to download and update the file directly from here:

dotnet file add https://github.com/awaescher/ObviousAwait/blob/master/ObviousAwait/ObviousExtensions.cs

Every now and then you can run dotnet file update to keep it up-to-date.


Of couse you can get awareness of the pitfalls with ConfigureAwait() to all of your co-workers. Accepting that little extra task of deciphering whether true or false was the right way to go in that particular case. However, I'd say:

Making things easy to get and hard to fail is always the way to go on the long run.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].