EventProxy使用学习笔记

介绍

EventProxy 仅仅是一个很轻量的工具,但是能够带来一种事件式编程的思维变化。有几个特点:
• 利用事件机制解耦复杂业务逻辑
• 移除被广为诟病的深度callback嵌套问题
• 将串行等待变成并行等待,提升多异步协作场景下的执行效率
• 友好的Error handling
• 无平台依赖,适合前后端,能用于浏览器和Node.js
• 兼容CMD,AMD以及CommonJS模块环境

安装

在页面中嵌入脚本即可使用:

<script src="https://raw.github.com/JacksonTian/eventproxy/master/lib/eventproxy.js"></script>
    // EventProxy此时是一个全局变量
    var ep = new EventProxy();

API

通过事件实现异步协作是EventProxy的主要亮点。除此之外,它还是一个基本的事件库。携带如下基本

• EventProxy

Examples:

var render = function (template, resources) {};
var proxy = new EventProxy();
proxy.all("template", "l10n", render);
proxy. emit ("template", template);
proxy. emit ("l10n", resources);

• on/addListener,绑定事件监听器
• removeListener,移除事件的监听器
• removeAllListeners,移除单个事件或者所有事件的监听器
• emit,触发事件
• once,绑定只执行一次的事件监听器
• immediate,绑定事件,并立即触发它,别名asap
• all,绑定一些事件,当所有事件执行后,将执行回调方法,别名assign
• fail,error事件处理
• tail,与all方法比较类似,都是注册到事件组合上。不同在于,指定事件都触发之后,如果事件依旧持续触发,将会在每次触发时调用handler,别名assignAll/assignAlways
• after,事件执行N次后,在执行回调函数
• any,事件执行完成后,只执行一次回调函数
• not,当事件名称与绑定的绑定事件名称不符时,才执行回调函数
• done,error事件处理
• create,创建EventProxy

Examples:

var ep = EventProxy.create();//等价于var ep = new EventProxy();
ep.all('user', 'articles', function(user, articles) {
  // do something...
});
// or one line ways: Create EventProxy and Assign
var ep = EventProxy.create('user', 'articles', function(user, articles) {
  // do something...
});

为了照顾各个环境的开发者,上面的方法多具有别名。
• YUI3使用者,subscribe对应的是on/addlistener,fire对应的是emit。
• jQuery使用者,trigger对应的是emit,bind对应的是on/addlistener。
• removeListener和removeAllListeners可以通过别名unbind完成。

异步协作

多类型异步协作

此处以页面渲染为场景,渲染页面需要模板、数据。假设都需要异步读取。

var ep = new EventProxy();
ep.all('tpl', 'data', function (tpl, data) {
  // 在所有指定的事件触发后,将会被调用执行
  // 参数对应各自的事件名
});
fs.readFile('template.tpl', 'utf-8', function (err, content) {
  ep.emit('tpl', content);
});
db.get('some sql', function (err, result) {
  ep.emit('data', result);
});

all方法将handler注册到事件组合上。当注册的多个事件都触发后,将会调用handler执行,每个事件传递的数据,将会依照事件名顺序,传入handler作为参数。

重复异步协作

此处以读取目录下的所有文件为例,在异步操作中,我们需要在所有异步调用结束后,执行某些操作。

var ep = new EventProxy();
ep.after('got_file', files.length, function (list) {
  // 在所有文件的异步执行结束后将被执行
  // 所有文件的内容都存在list数组中
});
for (var i = 0; i < files.length; i++) {
  fs.readFile(files[i], 'utf-8', function (err, content) {
    // 触发结果事件
    ep.emit('got_file', content);
  });
}

after方法适合重复的操作,比如读取10个文件,调用5次数据库等。将handler注册到N次相同事件的触发上。达到指定的触发数,handler将会被调用执行,每次触发的数据,将会按触发顺序,存为数组作为参数传入。

持续型异步协作

此处以股票为例,数据和模板都是异步获取,但是数据会是刷新,视图会重新刷新。

var ep = new EventProxy();
ep.tail('tpl', 'data', function (tpl, data) {
  // 在所有指定的事件触发后,将会被调用执行
  // 参数对应各自的事件名的最新数据
});
fs.readFile('template.tpl', 'utf-8', function (err, content) {
  ep.emit('tpl', content);
});
setInterval(function () {
  db.get('some sql', function (err, result) {
    ep.emit('data', result);
  });
}, 2000);

tailall方法比较类似,都是注册到事件组合上。不同在于,指定事件都触发之后,如果事件依旧持续触发,将会在每次触发时调用handler,极像一条尾巴。

异常处理

在异步方法中,实际上,异常处理需要占用一定比例的精力。在过去一段时间内,我们都是通过额外添加error事件来进行处理的,代码大致如下:

exports.getContent = function (callback) {
 var ep = new EventProxy();
  ep.all('tpl', 'data', function (tpl, data) {
    // 成功回调
    callback(null, {
      template: tpl,
      data: data
    });
  });
  // 侦听error事件
  ep.bind('error', function (err) {
    // 卸载掉所有handler
    ep.unbind();
    // 异常回调
    callback(err);
  });
  fs.readFile('template.tpl', 'utf-8', function (err, content) {
    if (err) {
      // 一旦发生异常,一律交给error事件的handler处理
      return ep.emit('error', err);
    }
    ep.emit('tpl', content);
  });
  db.get('some sql', function (err, result) {
    if (err) {
      // 一旦发生异常,一律交给error事件的handler处理
      return ep.emit('error', err);
    }
    ep.emit('data', result);
  });
};

EventProxy经过很多实践后,提供了优化的错误处理方案:

exports.getContent = function (callback) {
 var ep = new EventProxy();
  ep.all('tpl', 'data', function (tpl, data) {
    // 成功回调
    callback(null, {
      template: tpl,
      data: data
    });
  });
  // 添加error handler
  ep.fail(callback);

  fs.readFile('template.tpl', 'utf-8', ep.done('tpl'));
  db.get('some sql', ep.done('data'));
};

上述代码优化之后,开发者几乎不用关心异常处理。上述代码的转换,秘诀在fail方法和done方法中。

神奇的fail

ep.fail(callback);
// 由于参数位相同,它实际是
ep.fail(function (err) {
  callback(err);
});
// 等价于
ep.bind('error', function (err) {
  // 卸载掉所有handler
  ep.unbind();
  // 异常回调
  callback(err);
});
fail方法侦听了error事件,默认处理卸载掉所有handler,并调用回调函数。
神奇的done
ep.done('tpl');
// 等价于
function (err, content) {
  if (err) {
    // 一旦发生异常,一律交给error事件的handler处理
    return ep.emit('error', err);
  }
  ep.emit('tpl', content);
}

在Node的最佳实践中,回调函数第一个参数一定会是一个error对象。检测到异常后,将会触发error事件。剩下的参数,将触发事件,传递给对应handler处理。

done也接受回调函数

done方法除了接受事件名外,还接受回调函数。如果是函数时,它将剔除第一个error对象(此时为null)后剩余的参数,传递给该回调函数作为参数。该回调函数无需考虑异常处理。

ep.done(function (content) {
  // 这里无需考虑异常
});

本文转自:百度文库

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章