当前位置: 首页 > 科技观察

Python异步和JavaScript原生异步有什么区别?

时间:2023-03-11 22:47:15 科技观察

众所周知,JavaScript是单线程的,所以浏览器通过JavaScript发起的请求是异步请求。Python自带的asyncio模块为Python带来了原生的异步能力。在学习asyncio的时候,我们应该正确理解Python中的异步代码和JavaScript原生代码的区别,这样才能更好的理解用Python中的同步代码编写异步程序的逻辑。对于异步操作,如果我们使用日常生活中的例子,可能有助于我们理解JavaScript原生的异步操作,但可能会阻碍我们理解Python的异步操作。比如:我打开洗衣机,等洗衣机自动运转,我就可以做饭了,我可以一边等着米饭煮熟一边看书。现在假设我们要请求一个URL:http://httpbin.org/delay/5。请求此URL后,需要5秒才能返回结果。我们使用jQuery写了一段JavaScript代码:functiontest_async(){$.ajax({type:'GET',contentType:'application/json;charset=utf-8',url:'http://httpbin.org/delay/5',success:function(response){console.log('5秒请求返回:',response)}})vara=1+1a=a*2console.log(a)$.ajax({type:'GET',contentType:'application/json;charset=utf-8',url:'http://httpbin.org/ip',success:function(response){console.log('查询IP返回:',response)}})console.log('Thisistheendofthecode')}运行效果如下图所示:可以看出整个代码的执行逻辑和我们的异步是一致的生活。首先是一个5秒的Request,但是程序不会卡死等待,而是继续运行下面的代码,然后再发起新的请求。由于新请求返回时间短,所以新请求返回打印速度很快,打印5秒请求的结果是最后一个。就像我们打开洗衣机的电源,然后去淘米煮饭一样。我们把米放进电饭锅里,打开电饭锅的电源,就去看书了。最后先煮饭,再洗衣服。JavaScript原生异步请求的过程和日常生活中的逻辑非常相似。所以很容易理解JavaScript的异步流程。但在Python中,异步是另一回事。下面来写一段代码:importasyncioimportaiohttpasyncdefmain():asyncwithaiohttp.ClientSession()asclient:response=awaitclient.get('http://httpbin.org/delay/5')result=awaitresponse.json()print('5秒requestReturn:',result)a=1+1a=a*2print(a)new_response=awaitclient.get('http://httpbin.org/ip')new_result=awaitnew_response.json()print('查询IP返回:',new_result)print('代码到此结束')asyncio.run(main())运行效果如下图所示:可以看出程序还是串行运行的,并且有根本没有异步跟踪。要让程序异步运行,我们需要收集足够多的任务提交给asyncio,让它通过事件循环调度这些任务:importasyncioimportaiohttpasyncdefdo_plus():a=1+1a=a*2print(a)asyncdeftest_delay(client):response=awaitclient.get('http://httpbin.org/delay/5')result=awaitresponse.json()print('5秒请求返回:',result)asyncdeftest_ip(client):response=awaitclient.get('http://httpbin.org/ip')result=awaitresponse.json()print('查询IP返回:',result)asyncdeftest_print():print('代码到此结束')asyncdefmain():asynciohttp.ClientSession()asclient:tasks=[asyncio.create_task(test_delay(client)),asyncio.create_task(do_plus()),asyncio.create_task(test_ip(client)),asyncio.create_task(test_print())]awaitasyncio。gather(*tasks)asyncio.run(main())运行效果如下图所示:这是因为,在asyncio中,task是可以并行化的最小单元,需要将task组合在一起,通过asyncio.gather或syncio.wait提交到事件循环后,可以并行化。当使用代码asyncio.create_task(asynchronousfunction())时,这个异步函数实际上并没有运行,所以在上面的代码中:tasks=[asyncio.create_task(test_delay(client)),asyncio.create_task(do_plus()),asyncio.create_task(test_ip(client)),asyncio.create_task(test_print())]创建一个包含4个任务的列表,这4个异步函数中的代码还没有执行。当再次调用awaitasyncio.gather(*tasks)时,这4个任务作为4个参数传入asyncio.gather函数,于是Python的事件循环开始对它们进行调度。在这些异步函数中,其中包含await的地方是告诉Python,await后面的函数可能有IO等待,可以挂断等待一会,现在可以查看eventloop中的其他异步任务是否已经完成等待就可以了跑步。没有await的地方,还是串行的。比如do_plus中的三行代码是一次性依次执行的。因此,我们在使用Python的asyncio编写异步代码时,需要提前安排好异步切换位置,并打包为一个异步任务,然后一次性提交一批任务给asyncio,让Python自己执行按照我们安排的切换逻辑进行切换。安排这些任务。就像,当我在写JavaScript时,我先出去打开洗衣机,然后在等待的时间里想办法下一步做什么。当我写Python的时候,我需要提前安排好整个计划:先打开洗衣机,边洗米边煮饭,然后看书。并将这份计划提交给专门做事的人执行。只有了解了这个区别,才能更好的在Python中使用asyncio。注意,本文所说的异步JavaScript是JavaScript最原始的异步逻辑。JavaScript现在具有Promise等高级功能来实现类似于Python的异步逻辑。