【问题标题】:Use Asyncio with for loop in Python similar to map method and promise.all in Javascript在 Python 中使用带有 for 循环的 Asyncio,类似于 Javascript 中的 map 方法和 promise.all
【发布时间】:2019-07-31 17:29:02
【问题描述】:

我正在尝试在 Python 中异步运行 for 循环,就像您可以在 Javascript 中使用 map 方法和 promise.all 一样。我到处搜索如何做到这一点,但下面的代码仍然是同步运行的(一个接一个地运行,而不是让循环在完成之前的迭代时进行其他迭代,比如 promise.all 允许你)。任何帮助将不胜感激。

from jwt import scopes                                                   
from googleapiclient.discovery import build
from google.oauth2 import service_account
import json
import asyncio

key = 'file.json'
ID = 'ID'
rg = 'A1'

j2 = service_account.Credentials.from_service_account_file(key, scopes=scopes).with_subject('me@emial.com')

ar = []
cl = build('classroom', 'v1', credentials=j2)

def cour():
    co = []
    result1 = cl.courses().list().execute()
    courses = result1.get('courses', [])
    for cc in courses:
        co.append(cc['id'])
    return co

cco = cour()

async def main():
    async def subs2(i):
        await asyncio.sleep(0)
        result2 = cl.courses().courseWork().list(courseId=i).execute() 
        works = result2.get('courseWork', [])

        for work in works:
            result = cl.courses().courseWork().studentSubmissions().list(courseId=work['courseId'], courseWorkId=work['id']).execute()
            subs = result.get('studentSubmissions', [])

            for sub in subs:
                try:
                    ar.append(sub['assignedGrade'])
                    ar.append(sub['courseId'])
                    ar.append(sub['courseWorkId'])
                    ar.append(sub['userId'])
                except KeyError as name: 
                    pass

    coros = [subs2(i) for i in cco]
    await asyncio.gather(*coros)

if __name__ == '__main__':
    cour()
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())
    loop.close()

【问题讨论】:

  • 您要使哪个特定调用异步运行?除了sleep(0) 之外,您的内部协程subs2(i) 内部没有任何等待调用。所以它总是会同步运行。

标签: javascript python python-3.x python-asyncio google-apps


【解决方案1】:

我认为您误解了asyncio 提供并发的方式。它不会产生任何额外的线程或进程。事件循环以及在其上运行的所有协程都在单个线程中执行。为了获得并发性,您的协程需要 await 进行异步 I/O 的调用,或其他一些可以控制异步事件循环的操作。

在您的示例中,您尝试并发运行的协程实际上并没有执行任何异步 I/O。所以每次协程执行时,它都会阻塞事件循环,直到它完成。这意味着每个都将按顺序执行。为了获得并发,您需要使用对异步友好的库而不是当前使用的库 (googleapiclient),或者使用 loop.run_in_executor 将阻塞工作转移到后台线程。

【讨论】:

  • FWIW,看起来googleapiclient 没有与异步兼容的替代品:github.com/googleapis/google-api-python-client/issues/360
  • 感谢 dano,但我(当然还有其他人)会非常感谢对此做出更具体的回应。我知道我没有正确执行此操作,但正如我之前提到的,即使在 stackoverflow 上,我仍然没有找到解决此问题的具体方法。必须有一种方法可以在不使用线程或多处理的情况下执行“promise.all”过程。多处理在这里也不起作用。那是不行的。我已经试过了,当你调用 cour() 和 main() 时,Google 也会阻止它。 stackoverflow.com/questions/3724900/…
  • 我并没有尝试在代码本身中做任何需要“等待”的事情,而是我正在尝试为 Classroom 中的每门课程执行该过程,就像我可以在 Node.js 中轻松完成的那样。它需要做的是在阵列中的各种课程上运行它。我希望我能更好地解释自己。我所知道的是,类似的东西在 Node 中可以正常工作,但在这里却不行。
  • 是的,完全正确。如果您阅读了我留下的第一条评论中的 github 问题(以及与之相关的其他问题),他们会详细说明为什么他们没有提供与asyncio 一起使用的非阻塞 API。基本上,在它们仍然支持 Python 2.7 时实现它是不可行的。他们说,一旦他们在 2020 年放弃对 2.7 的支持,他们希望添加一个 asyncio 友好的 API。
  • 在 Python 中引入asyncio 导致了一些在它出现之前就已经存在的网络库的有趣的兼容性问题。除非您在 a very specific way 中构建您的库,它将协议与底层 I/O 层仔细分离,否则支持这两个异步 I/O 库(asynciotwistedtornado)通常需要大量工作以及使用传统阻塞 I/O 的应用程序的客户端。许多项目仍未赶上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-09
  • 1970-01-01
  • 2023-03-18
  • 2012-01-30
  • 1970-01-01
  • 1970-01-01
  • 2012-10-11
相关资源
最近更新 更多