nbs: 非阻塞式同步调用, 告别洋葱回调
发布于 9 年前 作者 kiliwak 3981 次浏览 最后一次编辑是 8 年前 来自 分享

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 实现协程功能, 可避免洋葱回调
  • 也无需使用额外的引用来调用 waitresume
  • 加强了错误处理(内部集成了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);
});

各种示例代码与文档, 可以访问npmgithub

各位感兴趣的可以看一下, 欢迎讨论, 轻拍:)

4 回复

本人项目上用的是Fibers的封装Fibrous,在GitHub上开源的库,好像和你的差不多

不过我对Fibrous这库有一个很难接受的地方,就是入侵性太强

@CoderIvan 这个库我看了下, 初看跟fibers自带的future库感觉一样, 都是对method的调用写法进行改造, 都太具入侵性了. 这样的写法我是无法接受 其实这类库(包括我分享的这个), 在新项目里使用, 从头开始, 也许问题都不大(如果能忍受Fibrous或Future这种改变方法调用写法的话). 但是如果是对现有项目进行改造, 那就很难玩了. 而用原生fibersnbs, 只要在调用链的最顶端加上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后, 还需要它. 这个模式把已经很复杂的异步搞得更难用了. 当然这只是我个人见解, 从这点来说的确是非主流了点

回到顶部