【问题标题】:Difference between async await in python vs JavaScriptpython 与 JavaScript 中的异步等待之间的区别
【发布时间】:2021-09-09 08:28:58
【问题描述】:

注意:这与多线程或多处理无关。这个问题是关于单进程和单线程的。

Python async.io 和 JavaScript async 都是单线程概念。

在 python 的 async.io 中,我们可以使用 async await 关键字来创建一个函数,这样当这个函数被多次调用(通过聚集)时,它们会同时执行。它的工作方式是,当遇到 await 关键字时,可以执行其他任务。正如这里所解释的,我们将 async await 关键字应用于我们希望并发执行的函数。然而,当这些任务同时运行时,主线程被阻塞了。

在 JavaScript 中,异步是从回调、promise、async/await 演变而来的。在主程序中,当遇到异步时,函数被发送到事件循环(函数执行开始的地方),主线程可以继续工作。任何后续的异步函数也会添加到事件循环中。在事件循环中,当函数执行遇到等待时,其他函数有机会执行,直到遇到等待。

要在 python 中获得这种行为,即允许主线程在执行子任务时继续,唯一的选择是多线程/多处理。因为一旦我们启动子线程/进程,直到我们调用.join,主线程就不会被阻塞。

无论如何,python 的 async.io 是否可以使主线程非阻塞?如果不是,那么这就是 JavaScript 和 python 中异步概念的根本区别吗?

【问题讨论】:

    标签: javascript python asynchronous async-await


    【解决方案1】:

    当遇到异步时,将函数发送到事件循环,主线程可以继续工作。

    这是接近,但不太正确。在 Javascript 中,在调用堆栈被清空之前不会停止执行 - await 关键字将暂停特定函数的执行,直到事件触发,与此同时,控制权返回给它的调用者。这意味着任何异步函数的第一部分都会在它被调用后立即执行(它不会立即放入事件循环中),并且只会在await 被命中时暂停。

    要在 python 中获得这种行为,即 - 允许主线程在执行子任务时继续,唯一的选择是多线程/多处理。

    这里的区别在于,默认情况下,Javascript 总是有一个事件循环,而 python 没有。换句话说,python 有一个用于异步编程的开/关开关,而 Javascript 没有。当您运行诸如loop.run_forever() 之类的东西时,您基本上是在打开事件循环,并且在事件循环重新关闭之前,执行将不会从您停止的地方继续。 (在这里称其为“线程”并不完全正确,因为它都是单线程的,正如您已经承认的那样。但我不确定正确的词是什么)

    您是在询问是否有办法让您的代码在启动事件循环后继续执行。我很确定答案是否定的,也不应该需要。事件循环开始后你想执行的任何事情都可以在事件循环中执行。

    如果您希望您的 python 程序更像 Javascript,那么您要做的第一件事就是启动一个事件循环,然后可以将任何进一步的逻辑放在事件循环执行的第一个任务中。在 Javascript 中,这个样板文件基本上是为您完成的,而您的源代码实际上是在事件循环中排队的第一个任务。

    更新:

    由于 Javascript 事件循环的工作方式似乎有些混乱,我将尝试进一步解释一下。

    请记住,事件循环只是一个系统,当某些事件发生时,可以排队等待线程不忙时运行的同步代码块。

    那么让我们看看事件循环对这样一个简单的程序做了什么:

    // This async function will resolve
    // after the number of ms provided has passed
    const wait = ms => { ... }
    
    async function main() {
      console.log(2)
      await wait(100)
      console.log(4)
    }
    
    console.log(1)
    main()
    console.log(3)
    

    当 Javascript 开始执行上述程序时,它会从排队的单个任务开始,即“当你不忙时运行这些东西”队列。这个项目就是整个程序。

    因此,它将从顶部开始,定义需要定义的任何内容,执行console.log(1),调用主函数,进入并运行console.log(2),调用wait(),这在概念上会导致后台计时器首先,wait() 将返回一个 promise,然后我们将其 await,此时我们立即返回 main 的调用者,不等待 main,因此继续执行 console.log(3),直到我们最终完成的文件。整个路径(从定义函数到console.log(3))是一个单一的、不可中断的任务。即使另一个任务排队,Javascript 也不会停止处理该任务,直到它完成了这块同步逻辑。

    稍后,我们的倒数计时器将结束,另一个任务将进入我们的队列,这将导致我们的 main() 函数继续执行。与之前相同的逻辑在这里适用 - 我们的执行路径可以进入和退出其他异步函数,并且只会在到达结束时停止,在这种情况下,主函数(即使点击 await 关键字实际上也不会构成这一行同步逻辑停止,它只是让它跳回调用者)。在调用堆栈被清空之前,单个任务的执行不会停止,并且当从异步函数继续执行时,调用堆栈的第一个条目会从该特定异步函数开始。

    Python 的 async/await 遵循同样的规则,除了在 Python 中,事件循环默认不运行。

    【讨论】:

    • 我在我的问题中更新了 javascript 部分。请你看看。另外,最初,在 JavaScript 部分中,我从未说过执行将暂停,直到遇到 await 关键字。
    • 因此,Javascript 中的“主”程序实际上并没有任何特殊处理。这只是事件循环中的另一个任务。您在 Javascript 中执行的第一行相当于 python 事件循环的第一个任务的第一行。
    • @variable 我更新了我的答案,试图更清楚地解释事件循环。希望这可以帮助解决一些困惑。
    • 谢谢你。在 js 中,在事件循环中放入 wait 后,控制权返回到 main。好的。问题1:那么控制什么时候回到console.log(4)?是在完成主线程之后还是在另一个后续函数中遇到 async 关键字还是有其他可能性?问题2:我们可以在主程序中编写await关键字来强制控制进入下一个任务吗?
    • 1.主要(初始)任务将运行完成并且不能被中断,与任何其他任务一样。一旦倒计时完成并且当前没有其他任务正在运行,就可以完成执行 main() 函数的任务。这可能是在 main() 完成之后以及在等待其他函数调用之间。 2. 实际上是(假设此调用堆栈中的所有调用者也在等待此承诺)。
    猜你喜欢
    • 2019-12-03
    • 1970-01-01
    • 2019-04-03
    • 1970-01-01
    • 1970-01-01
    • 2021-11-07
    • 2019-02-14
    • 2020-11-08
    相关资源
    最近更新 更多