Javascript 与 C# 中的异步/等待

Hem*_*gat 12 javascript c# asynchronous async-await

我试图了解异步编程并遇到 async/await 关键字。我陷入了理解 async/await 关键字的使用中。实际上,我研究了两种编程语言:JavaScript 和 C#,发现两种语言中 async/await 关键字的使用存在很大差异。

对于 JavaScript 来说:

Async/await 使您的代码看起来同步,并在某种程度上使其行为更加同步。wait 关键字会阻止执行其后面的所有代码,直到 Promise 完成为止,这与同步操作完全相同。

链接:https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await# :~:text=Async%2Fawait%20makes%20your%20code,would%20with%20a%20synchronous%20操作。

所以,它说 async/await 将使执行同步。

对于 C# 来说:

async 关键字将方法转变为异步方法,这允许您在其主体中使用await 关键字。当应用await关键字时,它会挂起调用方法并将控制权交还给其调用者,直到等待的任务完成。

链接: https: //learn.microsoft.com/en-us/dotnet/csharp/async# :~:text=The%20async%20keyword%20turns%20a,使用%20inside%20an%20async%20方法。

因此,它说使用 async/await 将使代码执行异步。

我想问一下,JavaScript和C#中async/await关键字的使用真的有区别吗?

或者,

上述陈述中是否遗漏了什么?

Man*_*ani 12

Javascript 文档说“它使您的代码看起来同步”,但没有说“它使您的代码同步”。

就行为而言,async/await 中的 javascript 和 c# 没有区别。

async 关键字表示该方法中有一个异步操作。wait 关键字有助于将 CPS(连续传递样式)编码成看起来像同步的代码。CPS 类似于在 javascript 中在 Promise 之后使用 then() 或在 C# 任务中使用ContinueWith()。

“await”之前的任何代码都在当前线程下同步运行。当执行到“await”时,等待的操作在新线程(不一定是新线程,但假设是新线程)下开始,因此异步操作开始,并且当前线程被释放。当等待的操作结束时,执行将返回到“await”关键字中停止的位置并继续。

javascript 和 C# 的内部工作方式不同。

JavaScript 是事件驱动的。javascript 中有一个主事件循环和一个线程。当等待的操作完成时,会在后台引发一个事件,并且主单线程继续执行中断的异步函数。

在 C# 中,没有事件循环或单个线程。我们要么应该使用手动线程,并在它们完成工作后显式等待并加入它们,要么应该使用 async/await 之类的东西来代表我们管理线程和延​​续管理。使用 C# 的 async/await 内部使用的 TPL,使用回调将异步代码的延续传递给另一个任务。

无论如何,“await”关键字将复杂的嵌套 then() -js- 或ContinueWith() - c#- 链变成了看起来像普通同步代码的简单漂亮的代码。

function doSomethingCPS() {
   asyncOperation1()
      .then(r1 => { consume(r1); return asyncOperation2() })
      .then(r2 => { consume(r2); return asyncOperation3() })
      .then(r3 => { consume(r3); })
}
Run Code Online (Sandbox Code Playgroud)
async function doSomethingAsync() {
   var r1 = await asyncOperation1();
   consume(r1);
   var r2 = await asyncOperation2();
   consume(r2);
   var r3 = await asyncOperation3();
   consume(r3);
}
Run Code Online (Sandbox Code Playgroud)

这两个代码是等效的。但是,很明显后者更简单且更易于阅读。

javascript 和 c# 中的线程管理之间存在差异。

正如所说,在 javascript 中只有一个线程。如果由于任何原因阻塞,该页面将被阻塞。当浏览器在 20-30 秒后显示“页面没有响应”或“此页面中存在阻止页面的 JavaScript 代码”消息时。

HTML5 中引入了工作线程,有助于使用真正的独立线程。但这是另一个话题了。

你可能会问,如果 javascript 中只有一个线程,那么据说在另一个线程下工作的异步操作到底如何工作?!

好问题。在 javascript 中,虽然只有一个线程,但有些对象本身就使用单独的线程。计时器 - setInterval() 和 setTimeout() -、XMLHttpObject() 和 fetch() 是这方面的好例子。因此,在 javascript 中我们确实可以拥有异步代码。

最后一点是 C# 使用线程的方式。在 C# 中,async/await 使用名为 TPL(任务并行库)的库来工作。TPL 的核心有一个类似于异步任务的任务类。

我们真正应该知道的一点是,Task 相当于异步操作,但这并不一定意味着 Task 显式地使用单独的线程。TPL 中有一个任务调度程序可以控制内部线程的使用。如果任务的工作速度很快,使用单独的线程会浪费资源,因此任务将在当前线程下运行。

我们唯一应该知道的是任务是异步代码的逻辑单元。Fat中引入了Task,让我们免去手动线程管理的麻烦,这几乎是一个低级代码。

使用 async/await,我们摆脱了提供异步代码所需的所有样板代码。我们可以专注于我们的业务代码。


Gle*_*leb 4

我不熟悉 JavaScript,但这个声明:

Async/await 使您的代码看起来同步,并在某种程度上使其行为更加同步。wait 关键字会阻止执行其后面的所有代码,直到 Promise 完成为止,这与同步操作完全相同。

听起来非常适用于 C# async/await。对于这两种情况,您的代码看起来像是同步执行的,并且执行是顺序的。因为在 C# 中,当你有这样的代码时:

// ...
await FooAsync();
Console.WriteLine("Await has returned the execution");
Run Code Online (Sandbox Code Playgroud)

看起来好像您的执行线程正在运行FooAsync,然后同一个线程正在调用Console.WriteLine。而实际上,当执行线程命中时await,它会在幕后执行许多操作。这是一篇关于它的好文章。但在大多数情况下,

当应用await关键字时,它会挂起调用方法并将控制权交还给其调用者,直到等待的任务完成。

执行你的代码的线程将继续执行他的任务。然后当另一个(或同一个)线程完成Console.WriteLine时继续。FooAsync当您在 WPF 或 WinForms 应用程序中使用 UI 线程时,此行为非常有用。例如,FooAsync是一个非常重的方法。它会进行大量计算并需要大量时间才能完成。但是您在 UI 上运行代码,当用户单击按钮时,底层代码将由 UI 线程执行。FooAsync因此,如果您像这样同步运行和等待:

FooAsync().Result;
Run Code Online (Sandbox Code Playgroud)

你的用户界面将被“冻结”,用户将杀死你。所以当你去的时候

await FooAsync();
Run Code Online (Sandbox Code Playgroud)

FooAsyncUI 线程“要求”TaskScheduler通过任何可用线程运行。任务完成后,TaskScheduler 尝试执行下一行:

Console.WriteLine("Await has returned the execution");
Run Code Online (Sandbox Code Playgroud)

再次通过 UI 线程,

与同步操作完全相同。