wewea / wewea.github.io

Blog in issue:laughing:
0 stars 0 forks source link

EventProxy 学习笔记 #7

Open wewea opened 9 years ago

wewea commented 9 years ago
  1. 学习目的
  2. 掌握eventproxy的基本用法
  3. 掌握eventproxy的思想
  4. 学习写test


  1. 学习资料


  1. 正文

    API

/*
  ep.emit通知event事件已完成, 第二个参数是要处理的数据
*/
  ep.emit(event, data); 

/*
   ep.all 等待所有异步事件被触发,完成后执行callback
*/
  ep.all(event1, event2, event3, function(result1, resut2, result3){});

/*
  ep.after 适合重复触发的时间, 例如读取文件, 第一个参数是事件名, 第二个参数是重复次数, 第四个参数是callback, 其参数是所有触发事件的结果集合, 需注意如果第二个参数是某个数组的length属性, 该方法必须放置在数组非空之后的位置, 否则该方法一开始就被触发了, 因为其开始的长度为0, 则times为0
*/
  ep.after(event, times, function(results){});

/*
  ep.tail 与all不同之处在于在事件都触发之后, 如果还有时间持续触发, 仍然会调用handler, 而all不会.
*/
  ep.tail('event1', 'event2', handler);

/*
  ep.on/ep.addListener/ep.bind都是事件绑定监听器, 可以被触发多次, 要注意一点, 监听事件必须在emit之前, 否则emit是不能触发事件的, 因为emit是同步的
*/
  ep.on('event', listener);

/*
  ep.once 绑定只执行一次的监听器
*/
  ep.once('event', listener)

 /*
  ep.fail 'error'事件被触发时执行callback, 解除所有监听器, 并且调用异常回调, callback是处理异常的函数
 */
  ep.fail(callback)

/*
  ep.done 异步函数中的handler, 例如fs.readFile(fileName, ep.done('read_file'); 等价于fs.readFile(fileName, function(err, data){}), 有两种变形ep.done('event', fn(content)), ep.done(fn), 前者处理内容后返回即可, 后者需要手工emit
*/
   fs.readFile(fileName, ep.done('read_file'));

/*
  ep.group用于ep.after的方法, 相当于ep.done, 当时会对放回的结果进行排序
 例如fs.readFile(file, ep.group('read_file', function(data){})); 可以对文件内容处理后返回
*/
  ep.group('event', data); 
  ep.group('event', handler);

/*
  ep.emitLater 是ep.emit的异步版本
  ep.doneLater 是ep.done的异步版本
  以上两者都是为了避免event的监听器由于逻辑关系, 在emit之后才绑定, 导致的emit同步触发的时间没有被触发, 如下例event不会被触发, 只有使用doneLater才能使其延迟触发.
*/
  syncApi(ep.done('event'));
  ep.on('event');

/*
  另外还有ep.bindForAll, ep.removeListener, ep.any 等接口码如其名, 非常好懂, 可以去[API doc](http://html5ify.com/eventproxy/api.html)观摩学习
*/

示例代码

源码剖析

  1. 依赖 只依赖了debug, 可以根据名字输出不同颜色的debug信息
  2. 模块开头有个取反操作, 不知道有什么含义

     !(function(name, definition) {})('EventProxy', debug || function(){});
  3. 判断对AMD还是Node加载
  4. EventProxy()防止因为没有new 返回错误

    if (!this instanceof EventProxy)
    {
      return new EventProxy();
    }
  5. this._callback 维护了所有事件的监听器, key是事件名, value是array类型, 对应了某事件的所有监听器, trigger会从该属性获取监听器, 而bind函数则会往该属性添加监听器, 因此想要对应事件的被触发, 需要先bind, 再trigger
  6. 有许多方法别名, 例如emit是trigger的别名, 可以参考文档
  7. EventProxy有一个内置的事件'all'即ALL_EVENT, 对应bindForAll, unbindForAll等方法的监听器, trigger每次执行除了自己的事件还会检查是否有设置ALL_EVENT, 若有则执行其监听器
  8. tail和all函数都通过内部方法_assign进行事件的绑定, 内部有一个times计数器, 统计所有事件被触发的次数, 有一个flag对象判断事件是否已经被触发, 若未触发则执行times++, 从而实现tail和all的等待所有事件被触发才执行监听器的效果, _assign内部有一个_all函数, 该函数会在最后添加到ALL_EVENT, _all方法会检查触发的事件是否被已经在_assign中被注册若无则返回, 不执行监听器, _all函数内部会判断调用的是否是ep.all,若是执行unbindForAll(_all), 将_all监听器从ALL_EVENT的监听器列表中删除.
  9. tail和all最主要的区别是, ep.all内部是通过ep.once进行触发计数和数据保存的, ep.tail则是通过bind
  10. after方法可以在特定的事件触发N次后执行监听器, 其内部有一个all函数, 该函数被绑定到ALL_EVENT, 通过判断触发的事件名是否是指定的事件名, 来触发递减times的操作, times是after的第二参数, after内部有一个_after属性其保存了由group事件触发的数据及其触发的顺序
  11. group方法通过emit触发事件, 返回的数据有index和data两个属性
  12. done有三种形式
    • ep.done('event') // 由done来emit
    • ep.done('event', fn) // fn返回内容, 由done来emit
    • ep.done(fn); // 由fn来emit事件
  13. doneLater指示将done返回的结果由later函数执行之后, 再返回一个handler, 供异步函数调用
  14. later函数, setImmediate > process.nextTick > setTimeout, 由三者之一执行callback.
  15. 源码中将method.apply运用地出神入化.

感想: eventproxy充分地利用了Node事件驱动的特性, 由于编写Node程序需要使用大量的异步函数, 因此异步协作成了一个问题, 原始的写法导致大量的嵌套, eventproxy的出现使得程序员可以很舒服得组织异步代码的逻辑, 在看到doneLater把一堆复制的代码转换为一句代码, 简直爽.