python和JavaScript中的异步等待之间的区别
注意:这与多线程或多处理无关。这个问题是关于单进程和单线程的。
Python async.io 和 JavaScript async 都是单线程概念。
在 python async.io 中,我们可以使用 async await 关键字来创建一个函数,这样当这个函数被多次调用(通过gather)时,它们会被并发执行。它的工作方式是当遇到 await 关键字时,其他任务可以执行。正如这里所解释的,我们将 async await 关键字应用于我们想要并发执行的函数。然而,当这些任务并发运行时,主线程被阻塞。
在 JavaScript 中,异步是从回调、promise、async/await 演变而来的。在主程序中,当遇到 async 时,将函数发送到事件循环(函数执行开始的地方),主线程可以继续工作。任何后续的异步函数也会被添加到事件循环中。在事件循环中,当函数执行遇到 await 时,其他函数有机会执行,直到遇到 await。
要在 python 中获得这种行为,即 - 允许主线程在执行子任务时继续,唯一的选择是多线程/多处理。因为一旦我们启动了子线程/进程,直到我们调用.join主线程才不会被阻塞。
无论如何,python 的 async.io 可以使主线程非阻塞吗?如果不是,那么这是 JavaScript 和 python 中异步概念之间的根本区别吗?
回答
当遇到 async 时,函数被发送到事件循环,主线程可以继续工作。
这很接近,但并不完全正确。在 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() 将返回一个承诺我们然后await,在这一点上我们立即回到main的调用者,main没有等待所以执行继续console.log(3),直到我们最终在文件末尾完成。整个路径(从定义函数到console.log(3))是一个单一的、不可中断的任务。即使另一个任务排队,Javascript 也不会停止处理该任务,直到它完成这块同步逻辑。
稍后,我们的倒数计时器将完成,另一个任务将进入我们的队列,这将导致我们的 main() 函数继续执行。与以前相同的逻辑在这里适用 - 我们的执行路径可以进入和退出其他异步函数,并且只会在它到达主函数的末尾时停止(在这种情况下,即使点击 await 关键字实际上也不会使这一行同步逻辑停止,它只是让它跳回调用者)。单个任务的执行不会停止,直到调用堆栈被清空,并且当从异步函数继续执行时,调用堆栈的第一个条目从该特定的异步函数开始。
Python 的 async/await 遵循这些相同的规则,除了在 Python 中,事件循环默认不运行。