【问题标题】:Async/Await in Javascript vs C#Javascript 与 C# 中的异步/等待
【发布时间】:2021-05-20 14:47:52
【问题描述】:

我正在尝试了解异步编程并遇到了 async/await 关键字。我一直在理解 async/await 关键字的使用。我实际上查看了两种编程语言,JavaScript 和 C#,发现这两种语言在使用 async/await 关键字方面存在很大差异。

对于 JavaScript,它说:

Async/await 使您的代码看起来是同步的,并且在某种程度上它使其行为更加同步。 await 关键字会阻止执行它之后的所有代码,直到 promise 完成,就像它使用同步操作一样。

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

所以说 async/await 会使执行同步。

对于 C#,它说:

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

链接:https://docs.microsoft.com/en-us/dotnet/csharp/async#:~:text=The%20async%20keyword%20turns%20a,used%20inside%20an%20async%20method

所以,据说使用 async/await 会使代码执行异步。

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

或者,

上述陈述中是否缺少某些内容?

【问题讨论】:

  • So, its saying that async/await will make the execution synchronous. 这不是它所说的。它说它使代码 look 同步。
  • 如果您在字里行间阅读,您会发现它们都在说同样的事情。 "await 关键字阻止执行它后面的所有代码,直到 promise 完成""当应用 await 关键字时,它暂停调用方法并产生控制权。返回其调用者,直到等待的任务完成。”
  • await 不会阻塞线程。与此同时,其他操作仍然可以在其他地方发生(例如setInterval)。但是,below 行 await 将在 await 完成后执行。它不会使异步操作同步,它只是解决 Promises 回调地狱的一些很棒的语法糖。
  • async/await 在 C# 和 JavaScript 中的行为类似。事实上,C#Task 和 JavaScript Promise 在思想上是相似的。它们如何利用资源(线程)进行异步/等待操作(因为 C# 是多线程的,而 JS 是单线程的)以及对操作的控制(例如延迟启动、停止等)等其他差异,存在一些差异。但是,从编码/工程的角度来看,它们提供了相同的体验。
  • 在 JavaScript 中它说,The await keyword blocks execution of all the code that follows it until the promise fulfills, exactly as it would with a synchronous operation。与 C# 中的相反,When the await keyword is applied, it suspends the calling method and yields control back to its caller until the awaited task is complete.

标签: javascript c# asynchronous async-await


【解决方案1】:

我对 JavaScript 不熟悉,但这个说法:

Async/await 使您的代码看起来是同步的,并且在某种程度上它使它 表现得更加同步。 await 关键字阻止所有的执行 跟在它后面的代码,直到 promise 完成为止,和它完全一样 将与同步操作。

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

// ...
await FooAsync();
Console.WriteLine("Await has returned the execution");

似乎您的执行线程正在运行FooAsync,然后,同一个线程正在调用Console.WriteLine。而实际上,当执行线程到达await 时,它在幕后做了很多事情。 Here's a good article about it。但在大多数情况下,

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

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

FooAsync().Result;

您的用户界面将被“冻结”,用户会淘汰您。 所以当你去的时候

await FooAsync();

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

Console.WriteLine("Await has returned the execution");

再次由 UI 线程,

与同步操作完全一样。

【讨论】:

  • @variable 答案是 - 这取决于。 Async/await 生成一个状态机,根据等待的任务,该状态机可能最终由单个线程或多个线程执行。如果任务在等待时完成,则不会使用额外的线程。否则 - 任务调度程序将决定但通常涉及多个线程。我建议通过 Jeffrey Richter 的 C# 阅读 CLR。那里详细描述了该过程。
【解决方案2】:

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

在行为方面,javascript 和 c# 在 async/await 中没有区别。

async 关键字表示此方法中有一个异步操作。 await 关键字有助于将 CPS(延续传递样式)编码为看起来像同步的代码。 CPS 类似于在 promise 之后在 javascript 中使用 then() 或在 C# Tasks 中使用 ContinueWith()。

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

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

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

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

无论如何,'await' keywrods 将嵌套的 then() -js- 或 ContinueWith() - c#- 的复杂链变成了看起来像普通 -synchronous- 代码的简单漂亮的代码。

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

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

javascript和c#中的线程管理是有区别的。

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

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

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

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

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

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

我们唯一应该知道的是,Task 是异步代码的逻辑单元。在 fat 中引入了 Task,让我们摆脱了几乎是低级代码的手动线程管理。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-07-01
    • 1970-01-01
    • 2018-10-17
    • 2016-02-11
    • 2019-10-01
    • 2019-05-10
    • 2018-03-02
    • 1970-01-01
    相关资源
    最近更新 更多