简体   繁体   English

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

[英]Difference between async await in python vs JavaScript

Note: this is not about multi threading or multi processing.注意:这与多线程或多处理无关。 This question is regarding a single process and single thread.这个问题是关于单个进程和单个线程的。

Python async.io and JavaScript async both are single thread concepts. 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. 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. The way it works is that when an await keyword is encountered, other tasks can execute.它的工作方式是,当遇到 await 关键字时,可以执行其他任务。 As explained here we apply the async await keywords to function that we would like to execute concurrently.正如这里所解释的,我们将异步等待关键字应用于我们希望同时执行的 function。 However while these tasks are running concurrently, the main thread is blocked.然而,当这些任务同时运行时,主线程被阻塞。

In JavaScript async has evolved from callbacks, promise, async/await.在 JavaScript 中,异步从回调、promise、异步/等待演变而来。 In the main program, when async is encountered, then the function is sent to the event loop (where the function execution begins) and the main thread can continue working.在主程序中,当遇到异步时,将 function 发送到事件循环(function 执行开始的地方),主线程可以继续工作。 Any subsequent async function also gets added to the event loop.任何后续的异步 function 也会添加到事件循环中。 Inside the event loop when the function execution encountered an await then other function is given a chance to execute untill await in encountered.在事件循环中,当 function 执行遇到等待时,其他 function 有机会执行直到遇到等待。

To get this behaviour in python, that is - allow main thread to continue while executing child tasks the only option is multithreading/multiprocessing.要在 python 中获得此行为,即 - 允许主线程在执行子任务时继续,唯一的选择是多线程/多处理。 Because once we start the child thread/process, and untill we call .join the main thread is not blocked.因为一旦我们启动子线程/进程,直到我们调用.join主线程就不会被阻塞。

Is there anyway by which the python's async.io can make the main thread non blocking?无论如何,python的 async.io 可以使主线程非阻塞吗? If not, then is this the fundamental difference between async concept in JavaScript and python?如果不是,那么这是 JavaScript 和 python 中的异步概念之间的根本区别吗?

when async is encountered, then the function is sent to the event loop and the main thread can continue working.当遇到异步时,函数被发送到事件循环,主线程可以继续工作。

This is close , but not quite right.这很接近,但并不完全正确。 In Javascript, execution won't stop until the callstack has been emptied - the await keyword will suspend the execution of a particular function until an event triggers, and in the mean time, control returns to its caller.在 Javascript 中,在调用堆栈被清空之前不会停止执行 - await 关键字将暂停特定函数的执行,直到事件触发,与此同时,控制权返回给它的调用者。 This means the first part of any async function will execute as soon as it is called (it's not immediately put into the event loop), and will only pause as soon as an await is hit.这意味着任何异步函数的第一部分都会在它被调用后立即执行(它不会立即放入事件循环中),并且只会在await时暂停。

To get this behaviour in python, that is - allow main thread to continue while executing child tasks the only option is multithreading/multiprocessing.要在 python 中获得这种行为,即 - 允许主线程在执行子任务时继续,唯一的选择是多线程/多处理。

The difference here is that by default, Javascript always has an event loop and python does not.这里的区别在于,默认情况下,Javascript 总是有一个事件循环,而 python 没有。 In other words, python has an on/off switch for asynchronous programming while Javascript does not.换句话说,python 有一个用于异步编程的开/关开关,而 Javascript 没有。 When you run something such as loop.run_forever() , you're basically flipping the event loop on, and execution won't continue where you left off until the event loop gets turned back off.当你运行诸如loop.run_forever()类的东西时,你基本上是在打开事件循环,并且在事件循环被关闭之前,执行不会从你离开的地方继续。 (calling it a "thread" isn't quite the right word here, as it's all single-threaded, as you already acknowledged. but I'm not sure what the right word is) (在这里称它为“线程”并不完全正确,因为它都是单线程的,正如您已经承认的那样。但我不确定正确的词是什么)

You're asking if there's a way to let your code continue execution after starting up the event loop.您在询问是否有办法让您的代码在启动事件循环后继续执行。 I'm pretty sure the answer is no, nor should it be needed.我很确定答案是否定的,也不应该需要。 Whatever you want to execute after the event loop has started can just be executed within the event loop.在事件循环开始后你想执行的任何事情都可以在事件循环中执行。

If you want your python program to act more like Javascript, then the first thing you do can be to start up an event loop, and then any further logic can be placed within the first task that the event loop executes.如果你想让你的 python 程序更像 Javascript,那么你要做的第一件事就是启动一个事件循环,然后任何进一步的逻辑都可以放在事件循环执行的第一个任务中。 In Javascript, this boiler plate essentially happens for you, and your source code is effectively that first task that's queued up in the event loop.在 Javascript 中,这个样板基本上是为您发生的,您的源代码实际上是在事件循环中排队的第一个任务。

Update:更新:

Because there seems to be some confusion with how the Javascript event loop works, I'll try to explain it a little further.因为 Javascript 事件循环的工作方式似乎有些混乱,所以我将尝试进一步解释一下。

Remember that an event loop is simply a system where, when certain events happen, a block of synchronous code can be queued up to run as soon as the thread is not busy.请记住,事件循环只是一个系统,当某些事件发生时,一个同步代码块可以排队等待线程不忙时立即运行。

So let's see what the event loop does for a simple program like this:那么让我们看看事件循环对这样一个简单的程序做了什么:

// 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)

When Javascript begins executing the above program, it'll begin with a single task queued up in it's "run these things when you're not busy" queue.当 Javascript 开始执行上述程序时,它会从排队的单个任务开始,它的“当你不忙时运行这些东西”队列。 This item is the whole program.这个项目是整个程序。

So, it'll start at the top, defining whatever needs to be defined, executes console.log(1) , call the main function, enters into it and runs console.log(2) , calls wait() which will conceptually cause a background timer to start, wait() will return a promise which we then await , at which point we immediately go back to the caller of main, main wasn't awaited so execution continues to console.log(3) , until we finally finish at the end of the file.因此,它将从顶部开始,定义需要定义的任何内容,执行console.log(1) ,调用 main 函数,进入并运行console.log(2) ,调用 wait() 这在概念上会导致一个后台计时器启动,wait() 将返回一个承诺,然后我们await ,此时我们立即返回 main 的调用者,main 没有等待,因此继续执行console.log(3) ,直到我们最终在文件末尾完成。 That whole path (from defining functions to console.log(3) ) is a single, non-interruptible task.整个路径(从定义函数到console.log(3) )是一个单一的、不可中断的任务。 Even if another task got queued up, Javascript wouldn't stop to handle that task until it finished this chunk of synchronous logic.即使另一个任务排队,Javascript 也不会停止处理该任务,直到它完成了这个同步逻辑块。

Later on, our countdown timer will finish, and another task will go into our queue, which will cause our main() function to continue execution.稍后,我们的倒数计时器将结束,另一个任务将进入我们的队列,这将导致我们的 main() 函数继续执行。 The same logic as before applies here - our execution path could enter and exit other async functions, and will only stop when it reaches the end of, in this case, the main function (even hitting an await keywords doesn't actually make this line of syncrounous logic stop, it just makes it jump back to the caller).与以前相同的逻辑在这里适用 - 我们的执行路径可以进入和退出其他异步函数,并且只会在到达结束时停止,在这种情况下,主函数(即使点击等待关键字实际上也不会进入这一行同步逻辑停止,它只是让它跳回调用者)。 The execution of a single task doesn't stop until the callstack has been emptied, and when execution is continuing from an async function, the first entry of the callstack starts at that particular async function.在调用堆栈被清空之前,单个任务的执行不会停止,并且当从异步函数继续执行时,调用堆栈的第一个条目从该特定的异步函数开始。

Python's async/await follows these same rules, except for the fact that in Python, the event loop isn't running by default. Python 的 async/await 遵循同样的规则,除了在 Python 中,事件循环默认不运行。

javascript 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 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: await scope:

  • javascript: After the method is executed, wait for the Promise to resolve javascript:方法执行后,等待Promise解决
    • await wait(2) Just wait(2) inside is guaranteed to be synchronous (or wait) await wait(2)只是wait(2)里面保证是同步的(或者等待)
  • python: Suspend method for other methods to execute python:暂停方法让其他方法执行
    • await asyncio.sleep(2) Method read_file will release resources and suspend await asyncio.sleep(2)方法read_file会释放资源并挂起

btw, javascript's await/async is just Promise syntactic sugar顺便说一句,javascript 的await/async只是Promise语法糖

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

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