4.1函数式编程
4.2异步编程的优势与难点
4.2.1 优势
node带来的最大特性莫过于基于事件驱动的非阻塞I/O模型,这是他的灵魂所在。
在第3章中,我们讨论过node实现异步I/O的原理。利用事件循环的方式,js线程就像一个分配任务和处理结果的大管家,io线程池里的各个io线程都是小二,负责完成分配来的任务,小二与管家互不依赖,所以可以保持整体的高效率。缺点是管家无法承担过多的细节性任务,如果承担太多,则会影响到任务的调度,管家忙个不停,小二却得不到活干,结局是整体效率的降低。
4.2.2 难点
难点1:异常处理
try{
process.nextTick(cb);
}catch(e){
//todo...
}
尝试对异步方法进行try/catch操作只能捕获当次事件循环内的异常,对cb执行时抛出的异常无能为力。
node在处理异常上形成了一种约定,将异常作为回调函数的第一个实参传回,如果是空值,则表明异步调用没有异常抛出。
另一个容易犯的错误是对用户传递的回调函数进行异常捕获,示例代码如下:
try{
req.body=JSON.parse(buf,options.revier);
callback();
}catch(err){
err.body=buf;
err.status=400;
callback(err);
}
上述代码中,如果回调函数中出现异常,那么进入catch代码中执行,于是回调函数被执行了2次。正常的应该是:
try{
req.body=JSON.parse(buf,options.revier);
}catch(err){
err.status=400;
return callback(err);
}
callback();
在编写异步方法时,只要将异常正确地传递给用户的回调函数即可,无需过多处理。
4.3 异步编程解决方案
4.3.1 事件发布/订阅模式
node自身提供的events模块是对发布/订阅模式的一个简单实现。
node对事件发布/订阅的机制做了一些额外的处理。
侦听器超过10个,警告。Use emitter.setMaxListeners() to increase limit
如果触发了error事件,EventEmitter会检查是否对error事件添加过侦听器。如果添加了,交给侦听器处理,否则作为异常抛出。如果外部没有捕获这个异常,将会Error [ERR_UNHANDLED_ERROR]: Unhandled error
,引起线程退出。一个健壮的EventEmitter实例应该对error事件做处理。
1、继承events模块
node提供的核心模块中,有近半都继承了EventEmitter
2、雪崩
const events = require('events');
class MyEmitter extends events {}
let proxy=new MyEmitter();
proxy.setMaxListeners(0);
let status='ready';
let select =function (cb) {
proxy.once('selected',cb);
if(status=='ready'){
status='pending';
setTimeout(function () {
console.log('执行了。。。。');
proxy.emit('selected','xxxxx');
status='ready';
},100)
}
};
for(let i=0;i<100;i++){
select(function () {
console.log('.....')
});
}