在公司匆匆忙忙坑了半年多Node,对一些Node本身的机制不是很了解,打算重新梳理梳理. 也发布在 我的博客
Event有多重要?
**Node.js uses an event-driven, non-blocking I/O model that makes it lightweight and efficient. **
这是Node.Js官网对自身的介绍,明确强调了Node.Js使用了一个事件驱动、非阻塞式 I/O 的模型,使其轻量又高效。
而且在Node中大量核心模块都使用了Event的机制,因此可以说是整个Node里最重要的模块之一.
引入
Event模块中我们使用的都是EventEmitter
这个核心对象,当我们需要引入它时也极其简单
const EventEmitter = require('events').EventEmitter;
let ee = new EventEmitter();
如果需要自定义的EventEmitter,那么我们可以使用ES6 Class继承之后再创建对象
const EventEmitter = require('events');
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
基本使用
EventEmitter可以说是观察者模式的体现,所以实现概念可以理解为基于事件的监听和发布
EventEmitter.on()
/*
EventEmitter.on(event, listener) 为事件添加一个监听方法
event:监听事件的名字,String类型
listener:监听事件的回调函数
*/
ee.on('event', (a, b) => {
console.log(a, b);
// 输出结果: a b
});
EventEmitter.emit()
/*
EventEmitter.emit(event, args...) 触发监听器响应的事件
emit:监听事件的名字,String类型
args:触发时需要传递的参数
*/
ee.emit('event', 'a', 'b');
EventEmitter.once()
/*
EventEmitter.once(event, args...) 为事件添加一个监听方法,但只触发一次,触发后移除监听
event:监听事件的名字,String类型
listener:监听事件的回调函数
*/
ee.once('event', () => {
console.log(++m);
});
ee.emit('event');
//此处会正常log出1
ee.emit('event');
//此处会被忽略
错误处理
myEmitter.emit('error', new Error('Error'));
如果像这样直接抛出错误,会引发整个Node进程的崩溃
所以我们应该为错误处理也添加专用的监听器
ee.on('error', (err) => {
console.log('Some Error Happend');
});
ee.emit('error', new Error('Error!'));
这样就可以正确捕获到错误
试着干点啥
利用EventEmitter解决回调地狱
const EventEmitter = require('events').EventEmitter;
let ee = new EventEmitter();
ee.emit('step1',args1)
ee.on('step1',(args1) => {
//执行第一步,设返回值为args2,错误则emit(error)
ee.emit('step2',args2)
})
ee.on('step2',(args2) => {
//执行第二步,设返回值为argsXX,错误则emit(error)
ee.emit('stepXX',argsXX)//以此类推
})
//...
ee.on('error',(error) => {
//捕获错误处理
})
顶!
用这个解决回调地狱还不得累死,代码还不易读
EventDriven和EventEmitter其实是两回事
@FoghostCn 只是试着了解一下,自己实际业务还是用Promise和Co啦
@JacksonTian 恩自己这一块还不是很清楚所以想试着根据了解模块来了解Node的运行机制,能简要介绍一下他们的区别吗?我目前的印象是Node作为一个大的事件队列不断的监听和触发队列中的事件来驱动它
我现在把event当开发的pub/sub用,如果上production就换其它的。比如以下代码:
// bus.js
import EventEmitter from 'events';
import co from 'co';
let app = require('../../index');
class Emitter extends EventEmitter {}
const emitter = new Emitter();
export const publish = (eventName, data) => {
emitter.emit(eventName, data);
};
export const subscribe = (eventName, generator) => {
emitter.on(eventName, data=>co(function *() {
yield generator(data);
}).catch(error=>app.emit('error', error)));
};
这样在其它模块中,我就可以这样:
import bus from './bus'
function signIn() {
// ...
bus.publish('user-signed-in', {email, userId})
}
// email.js
import bus from 'bus'
bus.subscriber('user-signed-in', function *(data) {
yield email.send(data.user.email, {subject: 'welcome', body: 'welcome to cnode, ...'};
)
不知道,event对于跨node的情况是怎么样的?
以下代码来自某内部项目:
$.api
.get('/topic/item/:id/comments')
.group('comment')
.title('获取主题的评论列表')
.param('$user', {type: 'UserSession'})
.param('id', {type: 'TopicId', comment: '通过URL参数指定'})
.param('skip', {type: 'Number', comment: '查询评论的偏移量', default: 0})
.param('limit', {type: 'Number', comment: '查询评论的数量', default: 50})
.param('order_by', {
type: 'ListOrderBy',
params: {columns: ['id', 'created_at', 'updated_at']},
default: 'id:desc',
comment: '有效的排序字段有`id`, `created_at`, `updated_at`',
})
.use('check_topic_exists')
.register(async function (params) {
const query = {topic_id: params.id, is_removed: 0};
const count = await $.model.Comment.count(query);
let list = await $.model.Comment.list(query, params);
list = list.map($.model.Comment.formatPublic);
await $.model.User.fillUserInfo(list, 'author_id', 'author');
await $.model.CommentLike.fillLikesCount(list, 'id', 'like_count');
await $.model.Comment.fillReplyComment(list, 'reply_id', 'reply');
if (params.$user && params.$user.id) {
await $.model.CommentLike.fillLikedStatus(list, 'id', 'is_liked', params.$user.id);
}
return {count, list};
});
$.test.suite('Topic', function () {
const session = $.test.session();
const status = {};
before(async function () {
const ret = await session.post('/login')
.input({email: 'test1@example.com', password: '123456'})
.output.success();
assert.equal(ret.email, 'test1@example.com');
assert.equal(ret.role, 'user');
status.userId = ret.id;
});
it('post', async function () {
const ret = await session.post('/topic/new')
.input({
title: '这是第一篇文章',
content: '这里是内容',
tags: 'test,first',
})
.output.success();
console.log(ret);
assert.equal(ret.author_id, status.userId);
assert.equal(ret.title, '这是第一篇文章');
assert.equal(ret.content, '这里是内容');
});
});
可以说是Reactive Programming的核心。
其实event这个还是很灵活的,正因为太灵活了导致约束少了,写出来的代码稍不注意就很 go die,