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

luikore · 发布于 2013年05月17日 · 最后由 hlxwell 回复于 2013年05月23日 · 6280 次阅读
2880
本帖已被设为精华帖!

这周四的 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 并发

共收到 35 条回复
6430

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

186

还有 Promise/Future

96

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

2880

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

96

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之类的引进去

2880

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

96

#9楼 @luikore Erlang啊...

2880

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

96

#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

2880

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

2880

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

96

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

2880

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

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

96

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

2880

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

96

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

Erlang -> Core Erlang -> BEAM bytecode

parse_transform就可以了

Forms就是啦

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

96

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

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

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

2880

#21楼 @krazy

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

729

我们有神器 em-syncrony

2880

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

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

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

729

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

2622

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

729

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

2622

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

729

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

2622

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

729

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

2622

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

657

@krazy 碉堡……

239

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

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