繁体   English   中英

python 与 JavaScript 中的异步等待之间的区别

[英]Difference between async await in python vs JavaScript

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

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

In python, async.io, we can use async await keywords to create a function so that when this function is invoked multiple times (via gather) they get executed concurrently. 它的工作方式是,当遇到 await 关键字时,可以执行其他任务。 正如这里所解释的,我们将异步等待关键字应用于我们希望同时执行的 function。 然而,当这些任务同时运行时,主线程被阻塞。

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

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

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

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

这很接近,但并不完全正确。 在 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) ,调用 main 函数,进入并运行console.log(2) ,调用 wait() 这在概念上会导致一个后台计时器启动,wait() 将返回一个承诺,然后我们await ,此时我们立即返回 main 的调用者,main 没有等待,因此继续执行console.log(3) ,直到我们最终在文件末尾完成。 整个路径(从定义函数到console.log(3) )是一个单一的、不可中断的任务。 即使另一个任务排队,Javascript 也不会停止处理该任务,直到它完成了这个同步逻辑块。

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

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

javascript

const wait = async (s) => {
    setTimeout(() => {
        console.log("wating " + s + "s")
    }, s * 1000)
}
async function read_file() {
    console.log("initial read_file sleep(2.1)")
    await wait(2)
    console.log("read_file 1/2 wait(2)")
    await wait(0.1)
    console.log("read_file 2/2 wait(0.1)")
}
async function read_api() {
    console.log("initial read_api wait(2)")
    await wait(2)
    console.log("read_api whole wait(2)")
}
read_file()
console.log("does not block")
read_api()
console.log("the second time, won't block")
// initial read_file sleep(2.1)
// does not block
// initial read_api wait(2)
// the second time, won't block
// read_file 1/2 wait(2)
// read_api whole wait(2)
// read_file 2/2 wait(0.1)
// !!! Wait a moment
// wating 0.1s
// wating 2s 
// wating 2s

python

import asyncio

async def read_file():
    print("initial read_file asyncio.sleep(2 + 0.1)")
    await asyncio.sleep(2)
    print("read_file 1/2 asyncio.sleep(2)")
    await asyncio.sleep(0.1)
    print("read_file 2/2 asyncio.sleep(0.1)")

async def read_api():
    print("initial read_api asyncio.sleep(2)")
    await asyncio.sleep(2)
    print("read_api whole asyncio.sleep(2)")

async def gather():
    await asyncio.gather(
        asyncio.create_task(read_file()),
        asyncio.create_task(read_api()))
        
asyncio.run(gather())
"""
initial read_file asyncio.sleep(2.1)
initial read_api asyncio.sleep(2)
!!! Wait a moment
read_file 1/2 asyncio.sleep(2)
read_api whole asyncio.sleep(2)
read_file 2/2 asyncio.sleep(0.1)
"""

await scope:

  • javascript:方法执行后,等待Promise解决
    • await wait(2)只是wait(2)里面保证是同步的(或者等待)
  • python:暂停方法让其他方法执行
    • await asyncio.sleep(2)方法read_file会释放资源并挂起

顺便说一句,javascript 的await/async只是Promise语法糖

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM