0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Python协程与JavaScript协程的对比及经验技巧

马哥Linux运维 来源:从零开始的程序员生活 作者:从零开始的程序员 2021-10-20 14:30 次阅读

前言以前没怎么接触前端,对 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=yield1
print("fooa",a)
yield2
yield3
print("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())

"""
foostart
1
fooaa
2
3
foostart
1
foostart
1
foostart
fooaNone
fooend
"""

JavaScript 协程的进化

  • 同步代码

  • 异步 JavaScript: callback hell

  • ES6 引入 Promise/a+, 生成器 Generators(语法function foo(){}* 可以赋予函数执行暂停/保存上下文/恢复执行状态的功能), 新关键词 yield 使生成器函数暂停。

  • ES7 引入 async函数/await语法糖,async 可以声明一个异步函数(将 Generator 函数和自动执行器,包装在一个函数里),此函数需要返回一个 Promise 对象。await 可以等待一个 Promise 对象 resolve,并拿到结果

Promise 中也利用了回调函数。在 then 和 catch 方法中都传入了一个回调函数,分别在 Promise 被满足和被拒绝时执行,这样就就能让它能够被链接起来完成一系列任务。总之就是把层层嵌套的 callback 变成 .then().then()...,从而使代码编写和阅读更直观。生成器 Generator 的底层实现机制是协程 Coroutine。
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);//2
console.log(gen.next().value);//3
console.log(foo().next().value);//1
console.log(foo().next().value);//1

/*
foostart
1
fooaundefined
2
3
foostart
1
foostart
1
*/

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 框架基于这个库

例子

importasyncio
importtime


asyncdefexec():
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 组合的封装,使原先的异步代码在形式上更接近同步代码的写法,并且对错误处理/条件分支/异常堆栈/调试等操作更友好。

js 异步执行的运行机制

  1. 所有任务都在主线程上执行,形成一个执行栈。

  2. 主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。

  3. 一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列"。那些对应的异步任务,结束等待状态,进入执行栈并开始执行。

遇到同步任务直接执行,遇到异步任务分类为宏任务(macro-task)和微任务(micro-task)。当前执行栈执行完毕时会立刻先处理所有微任务队列中的事件,然后再去宏任务队列中取出一个事件。同一次事件循环中,微任务永远在宏任务之前执行。

例子

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
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10850

    浏览量

    211518
  • JAVA
    +关注

    关注

    19

    文章

    2964

    浏览量

    104686
  • 函数
    +关注

    关注

    3

    文章

    4326

    浏览量

    62557
  • 代码
    +关注

    关注

    30

    文章

    4774

    浏览量

    68505
  • 生成器
    +关注

    关注

    7

    文章

    315

    浏览量

    20999

原文标题:Python 协程与 JavaScript 协程的对比

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    FS-MCore-F800E STM32例

    FS-MCore-F800E STM32例
    发表于 11-18 15:10 0次下载

    使用TMS320C6416处理器:Turbo处理器(TCP)

    电子发烧友网站提供《使用TMS320C6416处理器:Turbo处理器(TCP).pdf》资料免费下载
    发表于 10-23 10:16 0次下载
    使用TMS320C6416<b class='flag-5'>协</b>处理器:Turbo<b class='flag-5'>协</b>处理器(TCP)

    使用TMS320C6416处理器:Viterbi处理器(VCP)

    电子发烧友网站提供《使用TMS320C6416处理器:Viterbi处理器(VCP).pdf》资料免费下载
    发表于 10-21 09:36 0次下载
    使用TMS320C6416<b class='flag-5'>协</b>处理器:Viterbi<b class='flag-5'>协</b>处理器(VCP)

    安达发|APS高级排高级物料需求计划

    APS高级排高级物料需求计划是在制造业中非常重要的概念。它们分别涉及到生产计划和物料管理,对于提高生产效率、降低成本和满足客户需求具有重要意义。下面我将详细介绍这两个概念及其在实际生产
    的头像 发表于 09-25 17:49 294次阅读
    安达发|APS高级排<b class='flag-5'>程</b>高级物料需求计划

    基于APS排系统的PDM功能

    APS系统(AdvancedPlanningandScheduling,先进计划与排)是一种基于APS系统(AdvancedPlanningandScheduling,先进计划与排)是一种
    的头像 发表于 09-21 16:53 235次阅读
    基于APS排<b class='flag-5'>程</b>系统的PDM功能

    国内APS高级排软件的全面解析

    在现代制造业和物流行业中,高效的生产计划和库存管理是企业提升竞争力的关键。随着信息技术的发展,高级排软件(AdvancedPlanningandScheduling,APS)成为了企业优化资源配置
    的头像 发表于 09-21 16:49 387次阅读
    国内APS高级排<b class='flag-5'>程</b>软件的全面解析

    晶泰科技与鑫集团签署战略合作协议

    近日,在苏州鑫能源中心,晶泰科技与鑫集团共同见证了双方战略合作新篇章的开启,正式签署了为期五年的战略合作协议。此次合作,不仅标志着两大行业巨头的强强联合,更预示着新能源材料研发领域将迎来一场由人工智能与自动化技术引领的深刻变革。
    的头像 发表于 09-03 14:34 472次阅读

    PGA308标定输出范围0.5v-4.5v满输出会漂移,为什么?

    PGA308标定输出范围0.5v-4.5v 满输出会漂移 (漂移范围正负20mv) 供电:5V 参考:4.096V 输入:0-130mv 输出:0.5-4.5v(满有漂移) 已测量外围电路电压都是稳定状态 请教出现满
    发表于 08-29 06:03

    声学测试扫频信号倍频频点计算(SoundCheck)

    声学测试中,经常需要用到激励信号,不管是测试扬声器还是麦克风,通常是输出一个标准信号源,然后采集信号与标准信号进行对比,以测试待测产品性能,标准音频可以有多种,参考Soundcheck 软件,如
    发表于 08-01 10:58

    求CS32A039例

    求CS32A039例,邮箱:448789893@qq.com
    发表于 07-31 16:54

    请问ESP32s3 ULP RISC-V处理器是否支持ADC的读取?

    我在ULP RISC-V处理器的例程中,没有发现有对ADC的操作,请问RISC-V处理器目前还不支持吗?使用的IDF版本为4.4.2。 我想在ULP模式下,通过ADC来读取外部器件的数据。
    发表于 06-14 07:38

    中软国际与河南软企业家共同探讨鸿蒙生态新未来

    近日,河南软企业家交流会——“鸿蒙千帆启航 创新提质生产”主题活动在郑州举行
    的头像 发表于 05-08 09:24 474次阅读

    鸿蒙轻内核源码分析:MMU 处理器

    1、 ARM C15 处理器 在 ARM 嵌入式应用系统中, 很多系统控制由 ARM CP15 处理器来完成的。CP15 处理器包含编号 0-15 的 16 个 32 位的寄存器。例如,ARM
    的头像 发表于 02-20 14:28 579次阅读
    鸿蒙轻内核源码分析:MMU <b class='flag-5'>协</b>处理器

    谈谈的那些事儿

    随着异步编程的发展以及各种并发框架的普及,作为一种异步编程规范在各类语言中地位逐步提高。我们不单单会在自己的程序中使用,各类框架如fastapi,aiohttp等也都是基于异步
    的头像 发表于 01-26 11:36 1109次阅读
    谈谈<b class='flag-5'>协</b><b class='flag-5'>程</b>的那些事儿

    BlueNRG系列处理器实战经验简介

    BlueNRG 系列芯片从最早的一代 BlueNRG-MS 开始就支持处理器模式。
    的头像 发表于 01-05 18:16 1842次阅读
    BlueNRG系列<b class='flag-5'>协</b>处理器实战<b class='flag-5'>经验</b>简介