程序员

coroutine, promise, async, await

2018-06-12  本文已影响18人  正向反馈

结论:promise, async, await 是不完全的抽象,coroutine更适合异步开发,解放心智负担。

近来,nodejs, python的发展,有些看不大懂。
nodejs由于其异步io的纯粹,在web服务器上的超高性能,获得了极大的关注和发展。
其关键特性,异步处理这块上,是这样发展的

callback 有回调地狱的问题
promise 把回调树平展开来,代码更好阅读
generator 真以同步写异步
async,await 以同步的方式来书写异步的代码

python由于GIL的存在,在多线程处理上是没有未来的。
所以python的web服务器基本上走的都是多进程的路子。
多线程也好,多进程也好,都是上个时代的处理方式,解决c10k都够呛
梳理下python在web服务器上的发展:

多进程 例如 uWSGI 服务器。 性能其实也够用了
回调 例如tornado 没有很详细的了解, 知道性能很高,有回调地狱的问题
greenlet 技术 不了解有什么常用的服务器或者web框架出来
asyncio
python3.5也引入了async,await

总结一下

总之,io上,只有两种模型:

  1. 同步io
  2. 异步io

想拥有更高的性能,只有异步io可以解决。然后如下几个共识:

  1. 回调嵌套回调,真心不适合人写的,太乱
  2. promise只是展平了,看起来好点了,但是流程的流转,与异常的结合还是不好
  3. async, await只是promise的再包装,本质没变。babel对async,await的支持就是通过转化成promise来实现的

coroutine

之前看过一种说法,

coroutine保留了lisp的续延特性的精华

什么是coroutine

coroutine 是由用户调度的调用栈,即轻量线程

为什么说coroutine更适合异步开发,

举个例子

这是await版

async function selectPizza() {
  const pizzaData = await getPizzaData()    // async call
  const chosenPizza = choosePizza()    // sync call
  await addPizzaToCart(chosenPizza)    // async call
}

async function selectDrink() {
  const drinkData = await getDrinkData()    // async call
  const chosenDrink = chooseDrink()    // sync call
  await addDrinkToCart(chosenDrink)    // async call
}

(async () => {
  const pizzaPromise = selectPizza()
  const drinkPromise = selectDrink()
  await pizzaPromise
  await drinkPromise
  orderItems()    // async call
})()

这是lua的coroutine版

function selectPizza() 
  local pizzaData = getPizzaData()    // async call
  local chosenPizza = choosePizza()    // sync call
  addPizzaToCart(chosenPizza)    // async call
end

 function selectDrink() 
  local drinkData = getDrinkData()    // async call
  local chosenDrink = chooseDrink()    // sync call
 addDrinkToCart(chosenDrink)    // async call
end

(function ()
  selectPizza()
  selectDrink()
  orderItems()    // async call
end)()

可以看到,coroutine可以做到跟同步多线程一样的写法

再举个例子, 异步与map的结合

这是await版

var arr = [1, 2, 3, 4, 5];

var results = await Promise.all(arr.map(async (item)  => {
    await callAsynchronousOperation(item);
    return item + 1;
}));

这是lua的coroutine版

local arr = {1,2,3,4,5}
local results = arr.map(function (item) 
    callAsynchronousOperation(item)
    return item + 1
end)

两者功能并不完全一样

await版,写库函数的人,需要关心库函数是异步的,调用方也需要关心
所调用的函数是异步的,要用await去等待,并且声明调用了await的函数要用async修饰。
coroutine版可以做到真正的以同步的方式来写异步的代码。
同时,coroutine可以真正自然地与try-catch异常处理结合,而await需要做更多的处理

在人月神话中,就有提到,程序的主要复杂度是不可减的,
人脑是无法巨细无遗地面对所有复杂度的。 所以程序架构的本质是管理并屏蔽复杂度
面向对象的三大特征,封装继承多态中,最有用的是
* 封装 屏蔽复杂度
* 多态 归一化处理

基于以上的认识,我们知道,coroutine真正的屏蔽了异步带来的复杂度和心智负担,这在实际开发中,帮助是巨大的。

大火的golang,相较于它的竞争者,优势突出,突出在于它的goroutine(特殊的coroutine)

推荐

上一篇 下一篇

猜你喜欢

热点阅读