首页 / 行业
Python协程与JavaScript协程的对比及经验技巧
2021-10-20 14:30:00
前言以前没怎么接触前端,对 JavaScript 的异步操作不了解,现在有了点了解。一查发现 Python 和 JavaScript 的协程发展史简直就是一毛一样!这里大致做下横向对比和总结,便于对这两个语言有兴趣的新人理解和吸收。
共同诉求随着 cpu 多核化,都需要实现由于自身历史原因(单线程环境)下的并发功能
简化代码,避免回调地狱,关键字支持
有效利用操作系统资源和硬件:协程相比线程,占用资源更少,上下文更快
什么是协程?总结一句话,协程就是满足下面条件的函数:
可以暂停执行(暂停的表达式称为暂停点)
可以从挂起点恢复(保留其原始参数和局部变量)
事件循环是异步编程的底层基石
混乱的历史Python 协程的进化
Python2.2 中,第一次引入了生成器
Python2.5 中,yield 关键字被加入到语法中
Python3.4 时有了 yield from(yield from 约等于 yield + 异常处理 + send), 并试验性引入的异步 I/O 框架 asyncio(PEP 3156)
Python3.5 中新增了 async/await 语法(PEP 492)
Python3.6 中 asyncio 库“转正” (之后的官方文档就清晰了很多)
在主线发展过程中,也出现了很多支线的协程实现如 Gevent。
deffoo():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.x的g.next()函数已经更名为g.__next__(),使用next(g)也能达到相同效果。#next()跟send()不同的地方是,next()只能以None作为参数传递,而send()可以传递yield的值.print(next(gen))print(gen.send("a"))print(next(gen))print(next(foo()))print(next(foo()))list(foo())"""foostart1fooaa23foostart1foostart1foostartfooaNonefooend"""
JavaScript 协程的进化
同步代码
异步 JavaScript: callback hell
ES6 引入 Promise/a+, 生成器 Generators(语法function foo(){}* 可以赋予函数执行暂停/保存上下文/恢复执行状态的功能), 新关键词 yield 使生成器函数暂停。
ES7 引入 async函数/await语法糖,async 可以声明一个异步函数(将 Generator 函数和自动执行器,包装在一个函数里),此函数需要返回一个 Promise 对象。await 可以等待一个 Promise 对象 resolve,并拿到结果
function*foo(){console.log("foostart")a=yield1;console.log("fooa",a)yield2;yield3;console.log("fooend")}constgen=foo();console.log(gen.next().value);//1//gen.send("a")//http://www.voidcn.com/article/p-syzbwqht-bvv.htmlSpiderMonkey引擎支持send语法console.log(gen.next().value);//2console.log(gen.next().value);//3console.log(foo().next().value);//1console.log(foo().next().value);//1/*foostart1fooaundefined23foostart1foostart1*/
Python 协程成熟体
可等待对象可以在 await 语句中使用,可等待对象有三种主要类型:协程(coroutine), 任务(task) 和 Future。协程(coroutine)
协程函数:定义形式为 async def 的函数;
协程对象:调用 协程函数 所返回的对象
旧式基于 generator(生成器)的协程
任务(Task 对象):
任务 被用来“并行的”调度协程, 当一个协程通过 asyncio.create_task() 等函数被封装为一个 任务,该协程会被自动调度执行
Task 对象被用来在事件循环中运行协程。如果一个协程在等待一个 Future 对象,Task 对象会挂起该协程的执行并等待该 Future 对象完成。当该 Future 对象 完成,被打包的协程将恢复执行。
事件循环使用协同日程调度: 一个事件循环每次运行一个 Task 对象。而一个 Task 对象会等待一个 Future 对象完成,该事件循环会运行其他 Task、回调或执行 IO 操作。
asyncio.Task 从 Future 继承了其除 Future.set_result() 和 Future.set_exception() 以外的所有 API。
未来对象(Future):
Future 对象用来链接 底层回调式代码 和高层异步/等待式代码。
不用回调方法编写异步代码后,为了获取异步调用的结果,引入一个 Future 未来对象。Future 封装了与 loop 的交互行为,add_done_callback 方法向 epoll 注册回调函数,当 result 属性得到返回值后,会运行之前注册的回调函数,向上传递给 coroutine。
几种事件循环(event loop):
libevent/libev:Gevent(greenlet + 前期 libevent,后期 libev)使用的网络库,广泛应用;
tornado:tornado 框架自己实现的 IOLOOP;
picoev:meinheld(greenlet+picoev)使用的网络库,小巧轻量,相较于 libevent 在数据结构和事件检测模型上做了改进,所以速度更快。但从 github 看起来已经年久失修,用的人不多。
uvloop:Python3 时代的新起之秀。Guido 操刀打造了 asyncio 库,asyncio 可以配置可插拔的event loop,但需要满足相关的 API 要求,uvloop 继承自 libuv,将一些低层的结构体和函数用 Python 对象包装。目前 Sanic 框架基于这个库
例子
importasyncioimporttimeasyncdefexec():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: 意味着操作失败。
async、await语法糖
async、await 是对 Generator 和 Promise 组合的封装,使原先的异步代码在形式上更接近同步代码的写法,并且对错误处理/条件分支/异常堆栈/调试等操作更友好。js 异步执行的运行机制
所有任务都在主线程上执行,形成一个执行栈。
主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。
例子
varsleep=function(time){console.log("sleepstart")returnnewPromise(function(resolve,reject){setTimeout(function(){resolve();},time);});};asyncfunctionexec(){awaitsleep(2000);console.log("sleepend")}asyncfunctiongo(){console.log(Date.now())c1=exec()console.log("-------1")c2=exec()console.log(c1,c2)awaitc1;console.log("-------2")awaitc2;console.log(c1,c2)console.log(Date.now())}go();
event loop 将任务划分:
主线程循环从"任务队列"中读取事件
宏队列(macro task)js 同步执行的代码块,setTimeout、setInterval、XMLHttprequest、setImmediate、I/O、UI rendering等,本质是参与了事件循环的任务
微队列(micro task)Promise、process.nextTick(node环境)、Object.observe, MutationObserver等,本质是直接在 Javascript 引擎中的执行的没有参与事件循环的任务
总结与对比
说明 | python | JavaScript | 点评 |
---|---|---|---|
进程 | 单进程 | 单进程 | 一致 |
中断/恢复 | yield,yield from,next,send | yield,next | 基本相同,但 JavaScript 对 send 没啥需求 |
未来对象(回调包装) | Futures | Promise | 解决 callback,思路相同 |
生成器 | generator | Generator | 将 yield 封装为协程Coroutine,思路一样 |
成熟后关键词 | async、await | async、await | 关键词支持,一毛一样 |
事件循环 | asyncio 应用的核心。事件循环会运行异步任务和回调,执行网络 IO 操作,以及运行子进程。asyncio 库支持的 API 较多,可控性高 | 基于浏览器环境基本是黑盒,外部基本无法控制,对任务有做优先级分类,调度方式有区别 | 这里有很大区别,运行环境不同,对任务的调度先后不同,Python 可能和 Node.js 关于事件循环的可比性更高些,这里还需需要继续学习 |
到这里就基本结束了,看完不知道你会有什么感想,如有错误还请不吝赐教。
原文链接:https://www.cnblogs.com/lgjbky/p/14759463.html
编辑:jq最新内容
手机 |
相关内容
创造多样信号的万能工具:函数/任意
创造多样信号的万能工具:函数/任意波形发生器,函数,波形,信号,工具,创造,时钟,函数/任意波形发生器是一种用于产生各种形状和频率的什么是合封芯片,它与单封芯片有何不
什么是合封芯片,它与单封芯片有何不同?,芯片,系统,升级,集成,接触,功能模块,合封芯片是指将多个芯片整合在一起封装成一个独立的ADV74智能时代的三大核心技术
智能时代的三大核心技术,时代,核心技术,智能,机器学习,数据,学习,智能时代是指在人工智能技术的推动下,人类社会进入了一个智能化的逐渐迸发市场潜力的接触觉传感器
逐渐迸发市场潜力的接触觉传感器,传感器,接触,触觉,手术器械,虚拟现实,感知,AD8042ARZ接触觉传感器是一种能够模拟人类触觉感知的技国产传感器有望在人形机器人领域实
国产传感器有望在人形机器人领域实现突破!,人形机器人,突破,传感器,国内,高精度,接触,近年来,随着人工智能和机器人技术的不断发展,人高压放大器在软体机器人领域的应用
高压放大器在软体机器人领域的应用,高压,控制,优化,接触,效和,低压,软体机器人是一种具有柔软结构的ADUC812BSZ机器人,其主要特点是什么是旋变传感器,旋变传感器的特点
什么是旋变传感器,旋变传感器的特点、原理、分类、操作规程及发展趋势,传感器,发展趋势,分类,精度,接触,长期,TPA6204A1DRBR旋变传感什么是伺服变压器?变压器检测时需要
什么是伺服变压器?变压器检测时需要检测哪几个方面?,要检,检测,伺服,性能,系统对,输出,伺服变压器(Servo Transformer)是一种特殊的变压