Во-первых, не совсем честно сравнивать C# с JavaScript - это разные языки с разным временем выполнения и разными асинхронными механизмами.
JavaScript не является многопоточным - он работает на одна нить; следовательно, фактически ничего не может делать асинхронно. Чтобы преодолеть это, среда выполнения JS использует событие l oop, которое позволяет вам различать код, который должен блокировать основной поток, и код, который не должен блокироваться (например, и AJAX вызов - после запроса http отправлено, JS ничего не может сделать, кроме как подождать, поэтому вместо этого он будет выброшен на событие l oop до тех пор, пока не будет возвращен HTTP-ответ, затем он получит событие l oop и начнет выполнение и код, который зависит от ответа). Ключевые слова asyn c и await фактически являются синтаксисом c сахаром для обертывания функциональности Promise: следующий код -
function async makeCall()
{
const response = await makeHttpCall(httpRequest);
console.log(response);
}
похож на (но не совсем то же самое, что и ) -
function makeCall()
{
makeHttpCall(httpRequest)
.then(response => console.log(response));
}
Обещайте, что это пример некоторого кода, который будет помещен в событие l oop - JavaScript, будет планировать выполнение этого кода, чтобы он мог запускаться асинхронно на одном thread.
В C# у нас фактически есть много потоков, с которыми нужно работать, поэтому мы можем выполнять операцию asyn c одновременно. Чтобы упростить эту задачу, C# предоставляет нам параллельную библиотеку задач (TPL) , которая предоставляет ряд API-интерфейсов, которые значительно упрощают работу с многопоточным и запланированным выполнением кода. https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-parallel-library-tpl
Точно так же, как JavaScript - TPL и C# дают нам одинаковый синтаксис c сахара для использования 'asyn c' и 'await', и поскольку C# является языком более низкого уровня, большее количество библиотек предоставляют разработчику две реализации для кода, который не должен блокировать текущий или вызывающий поток.
это заблокирует все выполнение до завершения операции (текущий и вызывающий поток)
HttpResponse response = MakeHttpCall();
это заблокирует текущий поток выполнения, но вернет выполнение в вызывающий поток
HttpResponse response = await MakeHttpCallAsync();
это запустит asyn c, но вернет задачу для отслеживания выполнения
Task responseTask = MakeHttpCallAsync();
// execute code here while we wait for the http response
HttpResponse response = await responseTask; // or responseTask.Result but not ideal
TPL (любой код C#, использующий тип Task ) решит, должен ли новый поток может быть создан или если код должен быть запланирован в текущем потоке с использованием контекста синхронизации.
Также может быть полезно подумать о типе Task в C# аналогично тип Обещание в JS (очевидно, не то же самое, но есть сходства)