由于 Bluebird 体积太大,重造了一个 Promise 库

2015-05-17 17:10:42 +08:00
 ysmood

项目地址:yaku

由于一些轻型项目只需要使用最基本 Promise 功能,比如移动端,而现有的库不是太大就是功能不够完善,比如我向这个库提的这个 issue 无法被采纳。

这个库浏览器支持到 IE5+,体积只有 3.2KB,无任何依赖,比其他同类库小很多(Bluebird / 73KB, ES6-promise / 18KB)。对性能和异常处理做了优化。单测覆盖率 100%。如果你想学习 Promise 细节,这个库会是个不错的起点,我专门为此写了一个仅 80 行的实现,更多细节可以去看看 readme。

也是看了这个帖子,有感而发。我就埋头做事,不发表任何评论。

5460 次点击
所在节点    分享创造
35 条回复
leecade
2015-05-17 19:08:59 +08:00
敢用
magicdawn
2015-05-17 19:33:18 +08:00
深入浅出node.js上有教简单实现!
ysmood
2015-05-17 19:40:25 +08:00
@magicdawn 我这个跟书上的不一样,一般书上的实现是跑不过那 800 多个单测的,我这个 80 行的是可以的,我这个可能更偏向工程实际些。对于想更深入了解的人可能有帮助。
laoyur
2015-05-17 19:44:38 +08:00
mark,关注下
楼主能贴一下跟bluebird的性能比较就更好了
ysmood
2015-05-17 19:59:04 +08:00
@laoyur readme 里已经有个和各个库的性能比较了,看那个表格,其中有 Bluebird,你甚至能自己跑下 benchmark,具体怎么运行 readme 里也有。不过更复杂情况的性能测试不是当前的开发重点,性能会之后慢慢优化。
otakustay
2015-05-17 21:05:42 +08:00
我们也撸了一个Promise库,与楼主共同分享相互学习下

https://github.com/ecomfe/promise

测试都是过Promise/A+的一个样,unhandledRejection错误处理也有,大小上大概这样

~ coffee -c -p yaku/src/yaku.coffee | uglifyjs | gzip -c | wc -c
2064

~ cat promise/src/*.js | uglifyjs | gzip -c | wc -c
2754

我们的库加了2个功能,第一是enhance.js增加了一些便携的方法,比如thenBind、thenGetProperty。第二是setImmediate作了setImmediate函数的shim来让异步更快,去掉这2个的话

~ cat promise/src//Promise.js promise/src//PromiseCapacity.js promise/src/main.js promise/src/then.js promise/src/util.js promise/src/hook.js | uglifyjs | gzip -c | wc -c
1799

我们现在在考虑的是:

1. 这样的库与原生的Promise的关系如何处理
2. 全局错误处理应该怎么做
3. Promise的cancel/abort应该怎么做

与楼主共同学习
ysmood
2015-05-17 22:11:13 +08:00
@otakustay 我这边除了 unhandled rejection,还有 long stack trace,这个也非常重要。另外你可以测下你的性能和我的差别。

我的这个库的目的就是不添加除了调试用的任何非原生接口,换句话说用我这个库,可以没有任何副作用的删除它,切换到使用原生的 Promise。

关于 cancel/abort 这个不是 Promise 应该解决的问题,ES6 的 community 已经应该讨论过这个问题了。
ysmood
2015-05-17 22:24:55 +08:00
@otakustay 另外你理解的 unhandled reject 似乎不对。比如我用你的库运行

Promise.reject(10).catch(function() {})

结果还是会打印 10,这不符合 ES6 spec 的规范。
otakustay
2015-05-17 22:50:16 +08:00
@ysmood

我特地去找了一下,unhandled reject在ES6中还没有定义,我想不同的人会有不同的理解,跟着BlueBird走可能是个不错的选择,但最终会如何暂时还不知道。我们确实使用了更为同步的方式略过reject到catch的这个过程,这是我们从性能上考虑的设计了

cancel/abort的问题现在还在讨论中,并没有被废弃吧,在ES7中会不会实现我是很期待的,事实上除开Promise,几乎各种语言中的异步库或者协程功能都会涉及到cancellation的功能,早晚都是逃不开的话题,特别是在SPA会越来越大行其道的当前前端环境下,没有cancellation功能的异步会很不好用

性能上我试了下,其实你的性能应该是劣势的,特别在没有setImmediate的环境下做密集测试,但不同机器也可能会有不同的结果……

最后,目的其实不一样,我们更希望除了shim之外,可以再做一些什么方便基于Promise的开发,如ES6范围内的Promise没有finally的功能,这是一个很大的缺憾,让不少的代码变成.then(foo, foo)并且不能使用匿名函数,略不方便。因此也拿出来和楼主的共同学习参考
otakustay
2015-05-17 22:55:36 +08:00
@ysmood long stack trace确实很重要,我们也去研究下怎么支持……
otakustay
2015-05-17 23:11:59 +08:00
@ysmood 我转了一圈回来,感觉在Chrome上支持async stack是很有必要的,但Node上的long stack我打算放弃,如果一个库的定位是原生Promise的shim的话,node 0.12已经自带Promise了,对于这种版本可控的环境来说我们就不打算再继续为其提供shim了

所以还是和你的思路有差异化:)
ysmood
2015-05-17 23:16:56 +08:00
@laoyur 所以这里为了问题的简化,只讨论 ES6,unhandled reject 这个我口误,是要符合现行 chrome 或者 firefox的处理方式,他们会在 gc 里处理它,我这边尽量模拟了这个过程,这很关键。

关于性能,我帮你跑了下,在 node 下差距很大:

Node v1.8.1
OS darwin
Arch x64
CPU Intel(R) Core(TM) i7-4850HQ CPU @ 2.30GHz
--------------------------------------------------------------------------------
yaku
total: 324ms
init: 215ms
resolution: 109ms
memory: rss - 115mb | heapTotal - 96mb | heapUsed - 76mb

pp
total: 2378ms
init: 1940ms
resolution: 438ms
memory: rss - 300mb | heapTotal - 278mb | heapUsed - 252mb

浏览器里没必要测性能,只有服务端 server 用 yaku 的时候这才是瓶颈,用户的 browser 里不可能跑重运算的。
ysmood
2015-05-17 23:23:48 +08:00
@laoyur 除了 shim 外的功能应该基于一个基础库开发。比如我会把 yaku 写到完善,然后基于它写个 yaku-ex 之类的,人们可以选择最适合需求的,而不是一股脑都放一起。

关于类似 finally 这种我很早就在 stackoverflow 上讨论过了,ES6 大概不会支持的见: http://stackoverflow.com/questions/26667598/will-javascript-es6-promise-support-done-api

你可以很容易在类似 yaku-ex 这样的库里实现一个。
ysmood
2015-05-17 23:30:38 +08:00
@laoyur 你这个在 node 下已经慢到跟 q(2755ms) 差不多了,Bluebird 最快 301ms,上面的测试越小越快。
ysmood
2015-05-17 23:40:57 +08:00
@laoyur 而且类似这种单测也和原生 Promise 都不一致:

new Promise(function () { throw 10 }).catch(function() {})

其实不是把 promises-aplus-tests 都跑过就万事大吉了,还是有很多浏览器的规范在里面的。
ysmood
2015-05-17 23:46:20 +08:00
抱歉 @laoyur 君,@错对象了,之前都是 @otakustay 才对。
phoenixlzx
2015-05-17 23:48:08 +08:00
给 ys 菊苣点赞

star 已送
otakustay
2015-05-18 00:08:04 +08:00
@ysmood 从一开始就没有打算让这样的Promise库在node中跑,所以node的性能对我的意义不大,原因很简单,node现在支持原生Promise所以没有使用的场景,我还是不怎么理解什么时候能在node中使用一个Promise shim库,因为node无法升级使用较新版本吗,无法使用较新node的时候又能引入一个第三方库吗……在我厂似乎还真没有这样的情况(考虑到node可以直接把binary做portable带上)

关于finally的支持你可以去esdiscuss.org找,基本上会在ES7来添加这个API,纯粹因为讨论时ES6已经进入RC没办法再添加方法了

确实我相信还存在一些(不少)的边界场景有BUG,也必然会不断跟进,多谢你提供的case,如果你有更全面的case不如我们合作来补充aplus-test?
ysmood
2015-05-18 00:19:03 +08:00
@otakustay 我这个库就在你厂的一些项目跑着呢。我 readme 第一句话就是说比原生的快,这就是 node 使用它的理由。比如此时此刻你厂用的一个门户级网站就是就是用的 node 0.8,你部门不代表你厂,不代表现实复杂世界吧?
otakustay
2015-05-18 00:23:50 +08:00
@ysmood 我当然理解,同时我们的库也以支持我们所能触及的应用为优先考虑呗……

话说我从来没有否定或者贬低你的东西的应用场景和优秀性吧……

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/191715

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX