JavaScript 异步 IO 在 EventMachine 和 NodeJS 的写法对比

luikore · 2013年05月17日 · 最后由 hlxwell 回复于 2013年05月23日 · 9828 次阅读
本帖已被管理员设置为精华贴

这周四的 ruby tuesday 听完 nodejs 介绍获益良多。也了解到了 nodejs 社区绞尽心思改进异步写法的努力,还有就是原生 nodejs 开发者其实不喜欢写同步风格的代码 -__- . javascript 处理异常要很小心,因为 catch 不像 ruby 那样能区分异常类型,一抓就会吞掉全部异常所以要尽量缩小 try 块...

一些名词很容易搞混:异步 (async) 基本和并发 (concurrent) 一个意思,但和并行 (parallel) 完全是两回事。eventmachine 和 nodejs 都是通过多进程实现并行,通过异步 IO 实现并发的。顺序不等于同步 (如果了解 Haskell 的 Monad 和 Arrow 就会有更多的理解), 并发不等于并行。

eventmachine

# 顺序读文件
EM.defer do
  File.read 'a'
  File.read 'b'
end

# 并发读文件
EM.defer do
  File.read 'a'
end
EM.defer do
  File.read 'b'
end
  • ruby 里文件 io 天生是顺序同步的
  • ruby 里允许混入同步操作的库或者函数,只要 defer 了就不会 block 住
  • defer 是线程池实现的,由于 GVL, defer 操作没有并行效果但有并发效果

异步网络 IO 就要用到 callback 了,但有 Fiber 这个工具在,可以用 Fiber.yield 实现依赖的顺序化

# 读 url1 成功后才读 url2
def read_urls url1, url2
  f = Fiber.current
  req = HttpRequest.new(url1).get
  req.callback { f.resume }
  req.errback { f.resume }
  Fiber.yield # 将控制权交给任务队列, 然后在 callback 完成后会回到这里继续执行
  return 'bad' if req.response_header.status != 200

  req = HttpRequest.new(url2).get
  req.callback { f.resume }
  req.errback { f.resume }
  Fiber.yield
  return 'bad' if req.response_header.status != 200
end

Fiber.new {
  read_urls url1, url2
}.resume

nodejs

太复杂没记住 ... 总的来说顺序 IO 有好多套方案... 我还记得的有几种:

  • 事件中心方案,和写 jquery / eventmachine 的默认写法相似
  • CPS 方案,多加一个 continuation 参数
  • 重编译+eval 方案 (老赵的 Wind) 看起来挺高端的,但是问题也很多:大量增加载入时间; js eval 的代码不像 ruby 那样可以指定文件和行号,不容易 debug; 要通过伪变量指示操作是顺序还是并发; 代码写的和做的事情不是一回事...
  • next() 方案

并发读在 node 就是天生的了,但是如果下一步操作依赖上几步的结果,就和顺序读一个思路。

另外不管哪个框架,数个文件并发读应该是比顺序读慢的... 并发读文件只是演示用... 最优的操作就是一个本地文件读和数个网络 IO 并发

请问帖子中列表要怎么写?就是行首的点

还有 Promise/Future

根本就不需要什么老赵那个坑爹的 wind.js,JavaScript 本来就能yield的啊,只是 V8 不支持罢了

#5 楼 @bhuztez 好吧,可以用 js-coroutine

yield 就快啦 http://wingolog.org/archives/2013/05/08/generators-in-v8 还有node-fibers

wind.js 文档又少而且他自己都说不维护了 synchronous-to-asynchronous-javascript-compilers-cps

node-core 本来是用的 promise 的,不过因为 callback 形式比较流行又换过去了 现在是把 promise 的实现,控制流管理之类的都放到 user-land 还有个 fantasy-land,想把 functor,monad 之类的引进去

不过感觉就是,这么强调异步的话,框架选 haskell 才对... 数据依赖不用手动管理世界都清净了,还有个小小优点是单进程能解决并行问题。

#10 楼 @bhuztez 是的,学习 erlang 的动力大增 XD

#11 楼 @luikore

There is no clear winner in the duel. I prefer Haskell, but I am biased and believe in static typing. Yet I like programming in Erlang - both languages are good from different perspectives.

http://jlouisramblings.blogspot.com/2010/04/haskell-vs-erlang-for-bittorent-clients.html

After 2 months of tuning on and off I went back to Erlang Unoptimized Erlang version as fast as Combinatorrent in practice

http://www.erlang-factory.com/upload/presentations/658/jlouis-euc-2012.pdf

#12 楼 @bhuztez 性能看情况吧,我用 ruby 写的 parser combinator 比 haskell parsec 快...

#12 楼 @bhuztez 但 haskell 可以说是 multi-paradigm 的,do-notation 里可以写 state 什么的很舒服...

#14 楼 @luikore scala 呢 也有 actor 可以用哇

#16 楼 @krazy scala 启动和编译太慢了函数签名就和 C++ boost 模板库一样变态,还要各种用 java 的库 (java 库的 API 你懂的), 还要考虑线程安全 (和库的线程安全) 的问题,eventmachine/nodejs 单线程完全不用管,erlang/haskell 没有 mutable state 的问题也完全不用管

再说,任何内建 XML 语法的语言都应该被踩...

@krazy 哈哈,能直接上 erlang 的时候还是直接上吧,scala 那个语法确实会把人搞疯掉。。。。

#15 楼 @bhuztez 话说 erlang 是有内建 API 可以把 lambda 的 AST 直接取出来搞的么?

#19 楼 @luikore http://ruby-china.org/topics/7732

Erlang -> Core Erlang -> BEAM bytecode

parse_transform 就可以了

Forms 就是啦

其实完全没必要的,你可以直接写个 Parser 生成 CoreErlang 或者 BEAM 的

#18 楼 @donnior #17 楼 @luikore 没实际在项目用过,就瞎问问.. 还以为有 XML 字面量是个善行呢...

val 可以声明 immutable 变量,还可以加 lazy, 函数加=>还可以 call by name... 是为了同时 hold 住两个范式的类型系统,所以函数签名不得已的很复杂?

actor 和模式匹配搭配使用也很方便,还有 akka 可以玩啊

#21 楼 @krazy

很久以前一个工程写到 4k 行左右编译时间就受不了了... 还有各种各样的问题归根是语言设计贪大求全造成的... 不用写 java 谁还用 scala 啊

我们有神器 em-syncrony

#23 楼 @ShiningRay 嗯嗯,最近想用 sinatra-synchrony + pg 写点东西,但是就不能用 activerecord 了?

https://gist.github.com/apeckham/3955222

还没尝试和 AR 一起用的 https://github.com/leftbee/em-postgresql-adapter

#24 楼 @luikore 这个跟 activerecord 没关系,主要是需要使用特殊的 activerecord 的 adapter,当请求数据库的时候要释放当前的 fiber

#25 楼 @ShiningRay 是因为 Fiber 最大只能存 4k 的原因吗?

#26 楼 @jjym 可以尝试用 ruby 2.0 Fiber 在 64 位机器上是 128K 32 位上是 64K stack

#27 楼 @ShiningRay 。。Celluloid 貌似也这问题..2.0 能加启动参数设置 stack 大小

#28 楼 @jjym 貌似简单的应用应该没问题,以前用 em-synchrony 跑 Rails 的话,如果是简单的应用还是可以跑得起来的,但跑公司的应用就不行了

#29 楼 @ShiningRay em-synchrony 还能跑 rails...?神器啊。。

#30 楼 @jjym 是啊!不过我没做过具体的性能测试~

#32 楼 @ShiningRay 不错。。star..

#11 楼 @luikore 那学啊赶紧啊。erlang 可好了。

sqsy 整理学习 EventMachine 的一些文章和帖子 提及了此话题。 09月08日 17:05
需要 登录 后方可回复, 如果你还没有账号请 注册新账号