nodejs上有很多用来解决callback-hell的包, 有的是做一点语法糖, 有的用到了generator, 有的从底层实现类似协程的方式. 如async, co, 各种promise, es7的async/await等. co4也要求结合promise使用了. 说实在的, 我不喜欢promise, 太麻烦, 思维习惯转不过来. 所以一直都是在用fibers来用于实际开发, 后来在项目中慢慢积累, 就出来了现在这个包: nbs.
先来个例子吧:
var nbs = require('nbs'), wait = nbs.wait, resume = nbs.resume;
function sleep(ms){
setTimeout(function(){
resume();
}, sec);
wait();
}
nbs.run(function(){
console.time('sleep');
//sleep 1s
setTimeout(resume, 1000);
wait();
//call the sleep method
sleep(1000);
//return a value from resume param
setTimeout(resume.bind(null, 'wake up'), 1000);
console.log(wait());//wake up
console.timeEnd('sleep');//almost 3000ms
});
跟fibers语法很像, 不过已经省去了额外的引用(Fiber.current), api也更加明朗. fibers设计上没有区分run/resume, 这里也给分开了.
主要特性
- 使用
run
/wait
/resume
实现协程功能, 可避免洋葱回调 - 也无需使用额外的引用来调用
wait
和resume
- 加强了错误处理(内部集成了domain), 可通过向resume传入err参数抛出异常
- 加强了嵌套调用. 你可以在第n层回调下调用resume, 也可以在一个异步中额外启动一个协程栈
- 对于同步回调, 也能正常地resume/wait
- 提供了一个express中间件
并发发起异步IO
function get(url, cb){//get the response data from url
//cb(err, data)
}
nbs.run(function(){
get('url1', resume);
get('url2', resume);
get('url3', resume);
//data1 may not be the url1's result! it can be url2/url3's result too.
//you should not rely on that. same to data2/data3
var data1 = wait()[1];
var data2 = wait()[1];
var data3 = wait()[1];
console.log(data1, data2, data3);
});
各位感兴趣的可以看一下, 欢迎讨论, 轻拍:)
本人项目上用的是Fibers的封装Fibrous,在GitHub上开源的库,好像和你的差不多
不过我对Fibrous这库有一个很难接受的地方,就是入侵性太强
@CoderIvan 这个库我看了下, 初看跟fibers自带的future库感觉一样, 都是对method的调用写法进行改造, 都太具入侵性了. 这样的写法我是无法接受
其实这类库(包括我分享的这个), 在新项目里使用, 从头开始, 也许问题都不大(如果能忍受Fibrous或Future这种改变方法调用写法的话).
但是如果是对现有项目进行改造, 那就很难玩了. 而用原生fibers
或nbs
, 只要在调用链的最顶端加上run包裹一下, 后面的整个调用链都可以直接用resume
/wait
来实现’同步’效果. 而且对于一个方法, 里面是不是用到resume
/wait
来实现同步, 调用者基本不用关心.
比如, 以前有个方法foo
, 采用readFileSync
读取文件内容. 后面改造后, 使用 resume
/wait
+readFile
实现, 对于调用者来说, 是没有变化的
@kiliwak 确实如你所说的,所以我觉得异步的解决方式还是随主流有个规范比较好,但我看大多数热门的开源NodeJS库,基本没看到有用Fibers的
我们项目也是因为历史原因,才一直沿用Fibrous
@CoderIvan 嗯, 这个我也看到了, 包括co都开始用Promise了. es7的async/await就是类似generator+promise的实现. 不过这些都需要在调用栈上包裹一下(开启协程栈或得到一个generator句柄). 所以我觉得, js上解决异步回调的思路就是调用栈入口包裹+栈暂停/恢复. 而主流的解决办法, 除了这个思路, 还把Promise也带进来了. 说实在的, Promise当初作为解决callback-hell的手段是有存在的道理的, 但我觉得没有必要在有了generator和async/await后, 还需要它. 这个模式把已经很复杂的异步搞得更难用了. 当然这只是我个人见解, 从这点来说的确是非主流了点