Представьте - используете вы ваше любимое и вдруг, неожиданно оно подвисает. Вы не можете взаимодействовать с ним и UI никак не обновляется, пока вдруг все не возвращается в нормальное русло. Звучит знакомо? Я уверен, что это уже случалось с вами. Зачастую это происходит от выполнения продолжительных операций в UI-потоке, который никак не может заботиться об интерфейсе, пока все операции не будут завершены.
Для обеспечения работы UI потенциально длительные операции должны быть выполнены асинхронно, в отдельных потоках. Здесь ничего нового, но многие приложения до сих пор выполняют операции в UI-потоке. Почему? Одна из причин в том, что асинхронное программирование неудобно. Это гораздо более сложнее, тяжелее писать, тестировать, поддерживать, и если не написать должным образом это может привести к снижению производительности и блокировкам.
На протяжении многих лет версии .NET предлагали постепенно совершенствующиеся модели которые пытаются уменьшить сложность асинхронного программирования.
Но так было до .NET 4.5 / C# 5 - наконец мы можем писать асинхронный код так же просто, как и обыкновенный, а компилятор уже сам сделает всю тяжелую работу за нас (страшновато звучит...). По факту асинхронный код может быть почти идентичным его синхронному аналогу. Теперь без лишних слов давайте рассмотрим примеры async/await