关于javascript:Python协程与JavaScript协程的对比

34次阅读

共计 5174 个字符,预计需要花费 13 分钟才能阅读完成。

前言

以前没怎么接触前端对 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

def foo():
    print("foo start")
    a = yield 1
    print("foo a", a)
    yield 2
    yield 3
    print("foo end")


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())

"""
foo start
1
foo a a
2
3
foo start
1
foo start
1
foo start
foo a None
foo end
"""

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("foo start")
    a = yield 1;
    console.log("foo a", a)
    yield 2;
    yield 3;
    console.log("foo end")
}

const gen = foo();
console.log(gen.next().value); // 1
// gen.send("a") // http://www.voidcn.com/article/p-syzbwqht-bvv.html SpiderMonkey 引擎反对 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

/*
foo start
1
foo a undefined
2
3
foo start
1
foo start
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 框架基于这个库

例子

import asyncio
import time


async def exec():
    await asyncio.sleep(2)
    print('exec')

# 这种会和同步成果始终
# async def go():
#     print(time.time())
#     c1 = exec()
#     c2 = exec()
#     print(c1, c2)
#     await c1
#     await c2
#     print(time.time())

# 正确用法
async def go():
    print(time.time())
    await asyncio.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)。
以后执行栈执行结束时会立即先解决所有微工作队列中的事件,而后再去宏工作队列中取出一个事件。同一次事件循环中,微工作永远在宏工作之前执行。

例子

var sleep = function (time) {console.log("sleep start")
    return new Promise(function (resolve, reject) {setTimeout(function () {resolve();
        }, time);
    });
};

async function exec() {await sleep(2000);
    console.log("sleep end")
}

async function go() {console.log(Date.now())
    c1 = exec()
    console.log("-------1")
    c2 = exec()
    console.log(c1, c2)
    await c1;
    console.log("-------2")
    await c2;
    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 引擎中的执行的没有参加事件循环的工作.

扩大浏览 Node.js 中的 EventLoop

总结与比照

阐明 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 对于事件循环的可比性更高些, 这里还需须要持续学习

到这里就根本完结了, 看完不晓得你会有什么感想, 如有谬误还请不吝赐教.
顺便一句,Python 的 asyncio 库的官网文档当初清晰了很多, 而不是像 Python3.5 前的文档让人看不懂更无从下手, 心愿前面能有更多的第三方库跟进, 开发出更多反对异步的库. 毕竟目前和 go 原生协程比起来还是有差距的.

参考

  • python asyncio 五颗星
  • 从 event loop 到 async await 来理解事件循环机制 五颗星
  • JS 的事件轮询 (Event Loop) 机制 五颗星
  • JavaScript 中的协程 五颗星
  • JavaScript yield next send
  • JavaScript 迭代器和生成器
  • JavaScript Promise
  • Python 异步历史
  • Python 协程技术的演进
  • JS 中的协程(Coroutine)
  • Node.js 事件循环,定时器和 process.nextTick()

正文完
 0