当前位置: 首页 > 后端技术 > Python

Python协程和JavaScript协程的比较

时间:2023-03-25 21:14:27 Python

前言之前对JavaScript的异步操作了解不多。大致做一个横向比较总结,方便对这两种语言感兴趣的新手理解和吸收。共同的诉求是多核CPU需要在自身历史原因(单线程环境)下实现并发功能,简化代码,避免Callbackhell,关键字支持有效利用操作系统资源和硬件:与线程相比,协程占用资源更少,上下文更快(称为挂点的表达式)可以从挂点恢复(保留其原始参数和局部变量)事件循环是异步编程的底层基石混沌历史Python协程的演化在Python2.2中,首先引入了生成器,在Python2.5中,在语法中加入了yield关键字。Python3.4有yieldfrom(yieldfrom约等于yield+异常处理+send),实验引入的异步I/O框架asyncio(PEP3156)Python3。5中添加了async/await语法(PEP492)。Python3.6中的asyncio库被“还原”(之后的官方文档会更清楚)。在主线的发展过程中,也出现了很多协程的支线实现,比如Geventdeffoo():print("foostart")a=yield1print("fooa",a)yield2yield3print("fooend")gen=foo()#print(gen.next())#gen.send("a")#print(gen.next())#print(foo().next())#print(foo().next())#在python3.x版本,python2.xg.next()函数已更名为g.__next__(),使用next(g)也可以达到同样的效果。#next()和send()的区别在于next()只能传递None作为参数,而send()可以传递yield的值。打印(下一个(gen))打印(gen.send(“a”))打印(下一个(gen))打印(下一个(foo()))打印(下一个(foo()))列表(foo())“""foostart1fooaa23foostart1foostart1foostartfooaNonefooend"""JavaScript协程的演进同步代码异步JavaScript:回调地狱ES6引入Promise/a+,生成器Generators(语法function*foo(){}可以让函数执行暂停/保存context/resume执行状态函数),新关键字yield使生成生成器函数暂停。ES7引入了asyncfunction/await语法糖,async可以声明一个异步函数(将Generator函数和自动执行器包装在一个函数中),这个函数需要返回一个Promise对象。await可以等待一个Promise对象解析并得到结果,回调函数也用在Promise中。then和catch方法都传入一个回调函数,分别在Promise完成和拒绝时执行,这样就可以链式完成一系列的任务。简而言之,就是把嵌套的回调改成.then().then()...,让代码的编写和阅读更直观。Generator的底层实现机制是协程Coroutine。function*foo(){console.log("foostart")a=yield1;console.log("fooa",a)yield2;产量3;console.log("fooend")}constgen=foo();console.log(gen.next().value);//1//gen.send("a")//http://www.voidcn.com/article/p-syzbwqht-bvv.htmlSpiderMonkey引擎支持发送语法console.log(gen.next().value);//2console.log(gen.next().value);//3console.log(foo().next().value);//1console.log(foo().next().value);//1/*foostart1fooaundefined23foostart1foostart1*/Python协程成熟的waitable对象可以用在await语句中,有三种awaitable对象,主要有两种类型:Coroutine、Task和Future。Coroutine:协程函数:定义形式为asyncdef的函数;协程对象:调用协程函数返回的对象。老式的基于生成器的协程任务(Task对象):任务用于“并行”调度协程。当通过asyncio.create_task()等函数将协程封装为任务时,协程将自动调度执行的Task对象用于在事件循环中运行协程。如果协程正在等待Future对象,Task对象将暂停协程的执行并等待Future对象完成。当Future对象完成时,包装的协程将恢复执行。事件循环使用协作调度:事件循环一次运行一个任务对象。任务对象等待Future对象完成,而事件循环运行其他任务、回调或执行IO操作。asyncio.Task继承了Future的所有API,除了Future.set_result()和Future.set_exception()。未来对象(Future):Future对象用于链接低级回调代码和高级异步/等待代码。写完没有回调方法的异步代码后,为了获取异步调用的结果,引入一个Future未来对象。Future封装了与循环的交互。add_done_callback方法向epoll注册一个回调函数。当result属性得到返回值时,会运行之前注册的回调函数,向上传递给协程。几种事件循环:libevent/libev:Gevent使用的网络库(前期greenlet+libevent,后期libev),应用广泛;tornado:由tornado框架本身实现的IOLOP;picoev:meinheld使用的网络(greenlet+picoev)库,体积小,轻量级。与libevent相比,改进了数据结构和事件检测模型,因此速度更快。但是从github上看,好像年久失修了,用的人不多。uvloop:Python3时代的新人。Guido创建了asyncio库。Asyncio可以配置一个可插拔的事件循环,但是需要满足相关的API要求。uvloop继承自libuv,并用Python对象包装了一些低级结构和函数。目前的Sanic框架都是基于这个库exampleimportasyncioimporttimeasyncdefexec():awaitasyncio.sleep(2)print('exec')#这种会和同步效果已经#asyncdefgo():#print(time.time())#c1=exec()#c2=exec()#print(c1,c2)#awaitc1#awaitc2#print(time.time())#asyncdefgo()的正确用法:print(time.time())awaitasyncio.gather(exec(),exec())#加入协程组统一调度print(time.time())if__name__=="__main__":asyncio.run(go())JavaScript协程成熟的本体Promise继续使用Promise。本质是一个状态机,用来表示一个异步操作的最终完成(或失败),以及它的结果值。它有三种状态:pending:初始状态,既不成功也不失败的状态。fulfilled:表示操作成功完成。rejected:表示操作失败。Promise最终会有两种状态,一种是成功,一种是失败。当pending变化时,Promise对象会根据最终状态调用不同的处理函数。async,await语法糖async,await是对Generator和Promise组合的封装,使得原来的异步代码在形式上更接近于同步代码,对错误处理/条件分支/异常堆栈/debugging.js等操作更加友好异步执行的运行机制1)所有任务都在主线程上执行,形成一个执行栈。2)除了主线程之外,还有一个“任务队列”。只要异步任务有运行结果,就会在“任务队列”中放入一个事件。3)一旦“执行栈”中的所有同步任务执行完毕,系统就会去读取“任务队列”。那些对应的异步任务结束等待状态,进入执行栈并开始执行。同步任务直接执行,异步任务分为宏任务和微任务。当当前执行栈执行完毕后,立即处理microtask队列中的所有事件,然后从macrotask队列中取出一个事件。在同一个事件循环中,微任务总是在宏任务之前执行。示例varsleep=function(time){console.log("sleepstart")returnnewPromise(function(resolve,reject){setTimeout(function(){resolve();},time);});};async函数exec(){等待睡眠(2000);console.log("sleepend")}asyncfunctiongo(){console.log(Date.now())c1=exec()console.log("-------1")c2=exec()console.log(c1,c2)awaitc1;console.log("--------2")等待c2;console.log(c1,c2)console.log(Date.now())}go();事件循环分任务:主线程循环从“任务队列”中读取事件宏队列(宏任务).js同步执行的代码块,setTimeout、setInterval、XMLHttprequest、setImmediate、I/O、UI渲染等,本质上都是参与事件循环的任务。Microqueue(微任务)Promise、process.nextTick(节点环境)、Object.observe、MutationObserver等,本质上都是在Javascript引擎中直接执行不参与事件循环的任务。延伸阅读Node.jspython中的EventLoop总结与比较JavaScript注释流程单进程单进程一致中断/恢复yield,yieldfrom,next,sendyield,next基本相同,只是JavaScript对于send不需要futureobjects(callback)包装)FuturesPromise解决回调,同样的思路generatorGenerator会yield被打包为协程。思路成熟后支持关键字async、awaitasync、await。asyncio应用程序的核心事件循环运行异步任务和回调,执行网络IO操作,并运行子进程。asyncio库支持很多API,可控性强。浏览器环境基本上是一个黑盒子,外面基本上是不可控的。任务有优先级,调度方式不同。这里有很大的不同。运行环境不同。时间表不同。Python在事件循环方面可能比Node.js更具可比性。你还需要在这里继续学习。到这里基本就结束了。不知道你看完之后有什么感想。请随时犯任何错误。启示。顺便说一句,Python的asyncio库的官方文档现在清晰多了,不像Python3.5之前的文档那样看不懂,难以入手。希望有更多的第三方库跟进,开发出更多支持异步的库。毕竟和gonative协程相比还是有差距的。参考pythonasyncio五星从事件循环到asyncawait了解事件循环机制五星JS事件循环(EventLoop)机制五星JavaScript中的协程FiveStarsJavaScriptyieldnextsendJavaScriptIteratorsandGeneratorsJavaScriptPromisePythonAsynchronousHistoryPythonCoroutineTechnologyEvolutionCoroutinesinJSNode.js事件循环、定时器和进程。下一个Tick()