Event模块初体验(一)
发布于 8 年前 作者 scarletmu 6033 次浏览 来自 分享

在公司匆匆忙忙坑了半年多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) => {
    //捕获错误处理
  })
9 回复

用这个解决回调地狱还不得累死,代码还不易读

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,

回到顶部