Nodejs 实现同步的方法
来源:互联网 发布:最短路径优先算法 编辑:程序博客网 时间:2024/06/06 18:04
转载自Nodejs: 异步?!我要同步!!!
同时给出http://www.cnblogs.com/lengyuhong/archive/2011/12/22/2290711.html供快速参考
Nodejs是一个事件驱动、异步非阻塞的Javascript平台。异步编程模型是它的主要特色,所以nodejs众多的第三方模块都提供了异步api。
这种方式有很多优点,但在某些情况下,会造成以下问题:
- 代码多层嵌套,难写难读
- 控制异步调用的先后顺序,很麻烦
具体如何这里就不多说了,相信每个使用过的人都深有感触,不然怎么会有那么多的模块存在的目的就是为了改善这一点呢?
- http://altjs.org/ 见Synchronous to Asynchronous (CPS)一节
- https://github.com/joyent/node/wiki/modules#wiki-async-flow
这些通过三种方式来实现:
- 通过nodejs的本身设施实现,如async,q,node-seq,step等
- 通过语言扩展,定义一些特有的关键字如await等,如tamejs,jscex。可惜多了一步编译
- 通过node-fibers项目提供的纤程,如fibrous,common-node,streamline等
经过近两天的查看与试用,我对其中几个比较感兴趣。我的标准是:
- 示例代码易读、易理解
- 在不同的环境中都能稳定运行(如mocha中)
- 能方便与已有模块的异步api一起使用
下面一一介绍。
Tamejs
https://github.com/maxtaco/tamejs
tamejs不是纯js,因为它增加了两个特有的东西:await/defer。通常把文件保存为.tjs,在运行前需要先编译为.js(也可以通过require某些库在运行期进行)。这里给一个代码示例:
for (var i = 0; i < 10; i++) {
await { setTimeout (defer (), 100); }
console.log ("hello");
}
代码看起来相当简洁,为了这个,我宁愿多一个编译步骤!
看看它转换的js代码是什么样的:
var tame = require(‘tamejs’).runtime;
var __tame_defer_cb = null;
var __tame_fn_0 = function (__tame_k) {
tame.setActiveCb (__tame_defer_cb);
var __tame_k_implicit = {};
var i = 0;
var __tame_fn_1 = function (__tame_k) {
tame.setActiveCb (__tame_defer_cb);
var __tame_fn_2 = function (__tame_k) {
tame.setActiveCb (__tame_defer_cb);
i ++
tame.callChain([__tame_fn_1, __tame_k]);
tame.setActiveCb (null);
};
__tame_k_implicit.k_break = __tame_k;
__tame_k_implicit.k_continue = function() { __tame_fn_2(__tame_k); };
if (i < 10) {
var __tame_fn_3 = function (__tame_k) {
tame.setActiveCb (__tame_defer_cb);
var __tame_fn_4 = function (__tame_k) {
tame.setActiveCb (__tame_defer_cb);
var __tame_defers = new tame.Deferrals (__tame_k);
var __tame_fn_5 = function (__tame_k) {
tame.setActiveCb (__tame_defer_cb);
setTimeout (
__tame_defers.defer ( {
parent_cb : __tame_defer_cb,
line : 2,
file : "d.tjs"
} )
, 100 ) ;
tame.callChain([__tame_k]);
tame.setActiveCb (null);
};
__tame_fn_5(tame.end);
__tame_defers._fulfill();
tame.setActiveCb (null);
};
var __tame_fn_6 = function (__tame_k) {
tame.setActiveCb (__tame_defer_cb);
console . log ( "hello" ) ;
tame.callChain([__tame_k]);
tame.setActiveCb (null);
};
tame.callChain([__tame_fn_4, __tame_fn_6, __tame_k]);
tame.setActiveCb (null);
};
tame.callChain([__tame_fn_3, __tame_fn_2, __tame_k]);
} else {
tame.callChain([__tame_k]);
}
tame.setActiveCb (null);
};
tame.callChain([__tame_fn_1, __tame_k]);
tame.setActiveCb (null);
};
__tame_fn_0 (tame.end);
看起来还是有点吓人的,不容易啊不容易。
非常可惜的是,在mocha中无法正常使用。看下面这段代码:
require(‘should’);
function inc(n, callback) {
setTimeout(function() {
console.log(‘### inc: ‘ + n);
callback(n+1);
}, 1000);
};describe(‘test’, function(){
it(‘show ok with tamejs’, function(){
console.log(‘### testing …’);
var result;
await { inc(1, defer(result)); }
console.log(‘result: ‘ + result);
result.should.equal(3);
});
});
不知为什么,提示测试通过,实际上inc函数完全没有运行。
提了个bug: https://github.com/maxtaco/tamejs/issues/30,希望能早日解决。
Common Node
https://github.com/olegp/common-node
Common node基于node-fibers,提供了同步风格的代码,但在内部利用了纤程,不会阻塞主线程。既可以使用同步风格的代码,又不会影响性能,看起来很不错。
这里有一个性能对比图,可见common-node的同步风格,对于性能的影响还是很小的:
common-node的定位应该与node是相同的,但在编程风格上正好相反,它提倡使用“同步”。也因此,对于已有的异步api的模块,它不能很好地直接使用,需要专门为它提供一个修改版。从README中可以看到,目前已经有一些项目支持common-node。可惜相比nodejs庞大的模块库来说,还是可怜了,所以common-node的发展前景还是有些不明朗。
当支持它的模块更多一些之后,也许会有更多人来尝试它的。
Fibrous
(修改:在实际使用中,发现该库与很多其它的库有冲突,出现各种各样的错误,故不再推荐。现在使用async替代,见下段)
https://github.com/goodeggs/fibrous
Fibrous将给每个对象增加一个sync和feature属性,给原有的各异步方法提供一个同步版。因为它也基于node-fibers,所以这个同步函数实际上将在一个纤程中执行,不会阻塞主线程。
它的代码写起来比较简洁,看示例:
var fibrous = require(‘fibrous’);
var crypto = require(‘crypto’);var random = fibrous(function(length) {
var buf = crypto.sync.randomBytes(length);
return buf.toString(‘hex’);
});random(4,function(err, result) {
console.log(result);
});
如果我们的代码被fibrous()包着,则可在内部调用其它对象的.sync中的某方法(该方法是原异步方法的同步版本),不需要传入回调函数,直接以返回值形式拿到。
如果我们的代码没有被fibrous包着,则还得老老实实传回调。但这不是大问题,因为大多数情况下,我们都可以用fibrous包起来。
再看看它与mocha结合使用:
require('should');var fibrous = require('fibrous');function inc(n, callback) { setTimeout(function() { console.log('### inc: ' + n); callback(null, n+1); }, 1000);};describe('test', function(){ it('show ok with tamejs', fibrous(function(){ var x = inc.sync.call(null, 5); console.log('x:'+x); }));});
注意it(‘..’, fibrous(..)),可以直接把测试部分给包起来,内部就可以使用inc的sync版了。执行这个测试,将正确打印出:
x:6
使用fibrous,虽然对js中的对象有些侵入(增加了sync和futrue属性),但是很多时候,可以让我们的代码更加简洁,这个代价是我完全可以承受的。我将在以后更多的情况下使用它,看看会不会有其它问题。
暂时不用再纠结于异步同步的问题了,因为所有能试的方法我基本上都试了一遍,如果连fibrous也不行,那我也就只能放弃,强迫自己去写回调了。
Async
https://github.com/caolan/async
前面说中了,fibrous在使用中,与很多库有冲突,无奈移除,只好再硬着头皮写嵌套。期间纠结得想换语言,直到用了async。
async设计得巧妙又周到,考虑到了很多场景,能够很大程度上消除嵌套。比如说三种常见的情况:
- 多个异步函数并行执行,并且在全部执行完以后进行某个操作:parallel
- 多个异步函数依次执行,函数之间没有数据传递:series
- 多个异步函数嵌套执行,每个外层都要把数据传给内层:waterfall
另外还有7种:)
还有一种常见场景,需要对一个数组中的每一个元素进行异步操作,这时又提供了十种操作:
- forEach
- map
- filter
- reject
- reduce
- detect
- sortBy
- some
- every
- concat
详细的使用方法,我正在边学习边写示例,发到github上:https://github.com/freewind/async_demo
- Nodejs 实现同步的方法
- NodeJS实现同步的方法
- nodeJS实现方法的同步效果(去异步)
- Nodejs同步实现方式
- nodejs,async同步执行方法
- NodeJS 异步变同步的方法遍历文件夹
- nodejs基于fibers实现同步
- nodejs代码的深层嵌套实现同步操作
- 线程的同步:通过同步方法实现
- 实现多线程同步的方法
- 实现线程同步的方法
- 实现线程同步的方法
- nodejs 把异步变为同步方法
- nodejs爬虫简易实现和jquery的each方法
- nodejs javascript 实现随机数种子的几种方法
- nodejs 的监听方法
- 利用bluebird的promise 实现nodejs http下载异步调用的同步逻辑
- 实现线程同步的几种方法
- source insight 添加.cc为C++源文件
- Java实现队列一:通过自身的LinkedList实现
- DM8168-ARM平台移植USB WIFI RTL8192CU驱动
- Android开发之PopupWindow
- sourceInsight3.5 工具配置
- Nodejs 实现同步的方法
- Objective-C 基础语法详解
- Java实现队列二:通过数组方式实现
- 部署web应用
- 快速排序----非递归
- 信息论与编码复习
- Android 如何调用外部 jar 包中的 Service
- TOUCHINPUT 结构
- 优先级运算