Giter Site home page Giter Site logo

eventproxy's Introduction

EventProxy Build Status NPM version English Doc

NPM

这个世界上不存在所谓回调函数深度嵌套的问题。 —— Jackson Tian

世界上本没有嵌套回调,写得人多了,也便有了}}}}}}}}}}}}。 —— fengmk2

EventProxy 仅仅是一个很轻量的工具,但是能够带来一种事件式编程的思维变化。有几个特点:

  1. 利用事件机制解耦复杂业务逻辑
  2. 移除被广为诟病的深度callback嵌套问题
  3. 将串行等待变成并行等待,提升多异步协作场景下的执行效率
  4. 友好的Error handling
  5. 无平台依赖,适合前后端,能用于浏览器和Node.js
  6. 兼容CMD,AMD以及CommonJS模块环境

现在的,无深度嵌套的,并行的

var ep = EventProxy.create("template", "data", "l10n", function (template, data, l10n) {
  _.template(template, data, l10n);
});

$.get("template", function (template) {
  // something
  ep.emit("template", template);
});
$.get("data", function (data) {
  // something
  ep.emit("data", data);
});
$.get("l10n", function (l10n) {
  // something
  ep.emit("l10n", l10n);
});

过去的,深度嵌套的,串行的。

var render = function (template, data) {
  _.template(template, data);
};
$.get("template", function (template) {
  // something
  $.get("data", function (data) {
    // something
    $.get("l10n", function (l10n) {
      // something
      render(template, data, l10n);
    });
  });
});

安装

Node用户

通过NPM安装即可使用:

$ npm install eventproxy

调用:

var EventProxy = require('eventproxy');
$ spm install eventproxy

Component

$ component install JacksonTian/eventproxy

前端用户

以下示例均指向Github的源文件地址,您也可以下载源文件到你自己的项目中。整个文件注释全面,带注释和空行,一共约500行。为保证EventProxy的易嵌入,项目暂不提供压缩版。用户可以自行采用Uglify、YUI Compressor或Google Closure Complier进行压缩。

普通环境

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

<script src="https://raw.github.com/JacksonTian/eventproxy/master/lib/eventproxy.js"></script>

使用:

// EventProxy此时是一个全局变量
var ep = new EventProxy();

SeaJS用户

SeaJS下只需配置别名,然后require引用即可使用。

// 配置
seajs.config({
  alias: {
    eventproxy: 'https://raw.github.com/JacksonTian/eventproxy/master/lib/eventproxy.js'
  }
});
// 使用
seajs.use(['eventproxy'], function (EventProxy) {
  // TODO
});
// 或者
define('test', function (require, exports, modules) {
  var EventProxy = require('eventproxy');
});

RequireJS用户

RequireJS实现的是AMD规范。

// 配置路径
require.config({
  paths: {
    eventproxy: "https://raw.github.com/JacksonTian/eventproxy/master/lib/eventproxy"
  }
});
// 使用
require(["eventproxy"], function (EventProxy) {
  // TODO
});

异步协作

多类型异步协作

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

var ep = new EventProxy();
ep.all('tpl', 'data', function (tpl, data) { // or 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作为参数。

快速创建

EventProxy提供了create静态方法,可以快速完成注册all事件。

var ep = EventProxy.create('tpl', 'data', function (tpl, data) {
  // TODO
});

以上方法等效于

var ep = new EventProxy();
ep.all('tpl', 'data', function (tpl, data) {
  // TODO
});

重复异步协作

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

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,极像一条尾巴。

基本事件

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

  • on/addListener,绑定事件监听器
  • emit,触发事件
  • once,绑定只执行一次的事件监听器
  • removeListener,移除事件的监听器
  • removeAllListeners,移除单个事件或者所有事件的监听器

为了照顾各个环境的开发者,上面的方法多具有别名。

  • YUI3使用者,subscribefire你应该知道分别对应的是on/addListeneremit
  • jQuery使用者,trigger对应的方法是emitbind对应的就是on/addListener
  • removeListenerremoveAllListeners其实都可以通过别名unbind完成。

所以在你的环境下,选用你喜欢的API即可。

更多API的描述请访问API Docs

异常处理

在异步方法中,实际上,异常处理需要占用一定比例的精力。在过去一段时间内,我们都是通过额外添加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,并调用回调函数。

神奇的 throw

throwep.emit('error', err) 的简写。

var err = new Error();
ep.throw(err);
// 实际是
ep.emit('error', err);

神奇的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) {
  // 这里无需考虑异常
  // 手工emit
  ep.emit('someevent', newcontent);
});

当然手工emit的方式并不太好,我们更进一步的版本:

ep.done('tpl', function (tpl) {
  // 将内容更改后,返回即可
  return tpl.trim();
});

注意事项

如果emit需要传递多个参数时,ep.done(event, fn)的方式不能满足需求,还是需要ep.done(fn),进行手工emit多个参数。

神奇的group

fail除了用于协助all方法完成外,也能协助after中的异常处理。另外,在after的回调函数中,结果顺序是与用户emit的顺序有关。为了满足返回数据按发起异步调用的顺序排列,EventProxy提供了group方法。

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', ep.group('got_file'));
}

group秉承done函数的设计,它包含异常的传递。同时它还隐含了对返回数据进行编号,在结束时,按顺序返回。

ep.group('got_file');
// 约等价于
function (err, data) {
  if (err) {
    return ep.emit('error', err);
  }
  ep.emit('got_file', data);
};

当回调函数的数据还需要进行加工时,可以给group带上回调函数,只要在操作后将数据返回即可:

ep.group('got_file', function (data) {
  // some code
  return data;
});

异步事件触发: emitLater && doneLater

在node中,emit方法是同步的,EventProxy中的emittrigger等跟node的风格一致,也是同步的。看下面这段代码,可能眼尖的同学一下就发现了隐藏的bug:

var ep = EventProxy.create();

db.check('key', function (err, permission) {
  if (err) {
    return ep.emit('error', err);
  }
  ep.emit('check', permission);
});

ep.once('check', function (permission) {
  permission && db.get('key', function (err, data) {
    if (err) {
      return ep.emit('error');
    }
    ep.emit('get', data);
  });
});

ep.once('get', function (err, data) {
  if (err) {
    return ep.emit('error', err);
  }
  render(data);
});

ep.on('error', errorHandler);

没错,万一db.checkcallback被同步执行了,在ep监听check事件之前,它就已经被抛出来了,后续逻辑没办法继续执行。尽管node的约定是所有的callback都是需要异步返回的,但是如果这个方法是由第三方提供的,我们没有办法保证db.checkcallback一定会异步执行,所以我们的代码通常就变成了这样:

var ep = EventProxy.create();

ep.once('check', function (permission) {
  permission && db.get('key', function (err, data) {
    if (err) {
      return ep.emit('error');
    }
    ep.emit('get', data);
  });
});

ep.once('get', function (err, data) {
  if (err) {
    return ep.emit('error', err);
  }
  render(data);
});

ep.on('error', errorHandler);

db.check('key', function (err, permission) {
  if (err) {
    return ep.emit('error', err);
  }
  ep.emit('check', permission);
});

我们被迫把db.check挪到最后,保证事件先被监听,再执行db.checkcheck->get->render的逻辑,在代码中看起来变成了get->render->check。如果整个逻辑更加复杂,这种风格将会让代码很难读懂。

这时候,我们需要的就是 异步事件触发

var ep = EventProxy.create();

db.check('key', function (err, permission) {
  if (err) {
    return ep.emitLater('error', err);
  }
  ep.emitLater('check', permission);
});

ep.once('check', function (permission) {
  permission && db.get('key', function (err, data) {
    if (err) {
      return ep.emit('error');
    }
    ep.emit('get', data);
  });
});

ep.once('get', function (err, data) {
  if (err) {
    return ep.emit('error', err);
  }
  render(data);
});

ep.on('error', errorHandler);

上面代码中,我们把db.check的回调函数中的事件通过emitLater触发,这样,就算db.check的回调函数被同步执行了,事件的触发也还是异步的,ep在当前事件循环中监听了所有的事件,之后的事件循环中才会去触发check事件。代码顺序将和逻辑顺序保持一致。 当然,这么复杂的代码,必须可以像ep.done()一样通过doneLater来解决:

var ep = EventProxy.create();

db.check('key', ep.doneLater('check'));

ep.once('check', function (permission) {
  permission && db.get('key', ep.done('get'));
});

ep.once('get', function (data) {
  render(data);
});

ep.fail(errorHandler);

最终呈现出来的,是一段简洁且清晰的代码。

注意事项

  • 请勿使用all作为业务中的事件名。该事件名为保留事件。
  • 异常处理部分,请遵循 Node 的最佳实践(回调函数首个参数为异常传递位)。

贡献者们

谢谢 EventProxy 的使用者们,享受 EventProxy 的过程,也给 EventProxy 回馈良多。

 project  : eventproxy
 repo age : 3 years, 6 months
 active   : 97 days
 commits  : 203
 files    : 24
 authors  :
   177  Jackson Tian            87.2%
     9  fengmk2                 4.4%
     7  dead-horse              3.4%
     2  azrael                  1.0%
     2  rogerz                  1.0%
     1  Bitdeli Chef            0.5%
     1  yaoazhen                0.5%
     1  Ivan Yan                0.5%
     1  cssmagic                0.5%
     1  haoxin                  0.5%
     1  redky                   0.5%

详情请参见https://github.com/JacksonTian/eventproxy/graphs/contributors

License

The MIT License。请自由享受开源。

捐赠

如果您觉得本模块对您有帮助,欢迎请作者一杯咖啡

捐赠EventProxy

eventproxy's People

Contributors

afc163 avatar alsotang avatar bitdeli-chef avatar cssmagic avatar dead-horse avatar fengmk2 avatar hqidea avatar iazrael avatar jacksontian avatar popomore avatar rogerz avatar yanxyz avatar yaoazhen avatar yuche avatar zhanhongtao avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eventproxy's Issues

eventproxy

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

关于命名空间

在绑定事件的时候,有没有增加命名空间呢。
类似于jQuery中
$('body').on('click.alert.data-api', dismiss, Alert.prototype.close)

如何处理多路重复触发呢?

我这儿有个需求,两个超大文件内容对比,所以不能一次性读入内存,是用的fs.createReadStream方法,程序大概意思是这样的。

var fs = require('fs');
var readline = require('readline');
var stream = require('stream');
var EventProxy = require('eventproxy');

var ep = new EventProxy();
ep.tail('line1','line2',function(line1, line2){
compare(line1,line2);
});

var instream1 = fs.createReadStream('a.txt');
var instream2 = fs.createReadStream('b.txt');
var outstream1 = new stream();
var outstream2 = new stream();
var rl1 = readline.createInterface(instream1, outstream1);
var rl2 = readline.createInterface(instream2, outstream2);
rl1.on('line', function(line) {
ep.emit('line1', line);
});
rl2.on('line', function(line) {
ep.emit('line2', line);
});

显然这个地方用ep.tail是不行的,如何实现?

after 乱序 返回数据的顺序没有按照执行顺序进行

    for (var i = 0; i < sqlarray.length; i++) {
     dbHandler(sqlarray[i],function(err,data){   //查询数据库
     if(err) throw err;
     let   result  =  data[0].result ;
      ep.emit('queryDb', result);
    });
    }
    ep.after('queryDb', sqlarray.length, function (list) {
      console.log(list);
      res.status(200).json({"result":list});
      // 所有文件的内容都存在list数组中
     });


这样之后,每次查询返回的数据顺序都不同。

[跨模块emit]_callbacks和_fired缓存起来

如果存在模块emit的情况,目前貌似只能,将EventProxy的实例在两个模块之间传递(或者模块的方法调用的时候传入)。
怎么处理跨模块emit的情况?
我在A里头监听,然后在B里头emit。同时A require B。
我只是一个刚使用eventproxy的,不清楚是否已经有跨模块的支持?

`addListener` does not check for duplication

If one callback is bound to one event multiple times, multiple instances of the callback would be stored, and unbind would only remove only one instance instead of all, which is not what the api describes.
It should be fixed ASAP.

关于express的res.send问题

场景:写一个后台接口(express),需要修改几张表,修改表的顺序分先后,所以用eventproxy控制并发。在ep.once或者ep.all的回调里调用res.send方法不生效,调用接口后得不到任何返回。有人遇到过这个问题么?求指导。

group bug

var ep = new EventProxy();
srcList.forEach(function(src) {
    fs.readFile(src, 'utf-8', read.group('files'));
});
ep.after('files', srcList.length, function() {
  //
});

Error:

Warning: Cannot read property 'request_group' of undefined Use --force to contin
ue.

另外,既然有doneLater了,是否也应该增加groupLater?

建议增强下group对错误信息的处理。

现在有个需求,group在出现的error的时候,需要知道是哪个index出现的错误。主要是为了fix掉它。

现在有三个想法:

  1. 在触发error的时候把index作为第二个参数传过去。
    image
  2. 在group保存一份error的list,并作为callback的第二个参数进行返回。
    image
  3. this上添加一个当前错误对象的index,比如 this.curErrorIndex 。
    image

不过前两种方案都对现在的接口有改变。

proxy.after的一个每次都emit一个数组的场景

我碰到一个场景,每次emit的data都是一个数组,并且这个数组最后要拿出来遍历后把所有子数组中的元素拿出来放到一起。
可以手动forEach然后concat实现。

加个类似于group的concat helper是否有用呢?

从 `.all()` 展开的一些思考

今天刚刚收到朴灵大神的九浅一深 NodeJS,读完异步一章有感而发。

简单说下情况

我们团队的某个产品涉及到了一系列的网络和本地 IO,用流程图表示大致上是这样:

2013-12-05 20 56 16

仔细观察这张图会发现,「更新远程资料」A/B/C 三个任务都在「更新绑定关系」之后,它们之后的「宣告完成」还需要有一条线连接「更新绑定关系」呢?这就是我喜欢 .all() 的一个原因:在无需将变量暂存到外面一层闭包的情况下,传递下一个任务不需要,但下下个任务需要的变量。

因此大概半年前发现 EP 后爱不_湿_手,弃 async.js 投 EP。

遇到的问题

或许要解决这个问题,并不能全靠典型的 EventEmitter 模型。

.all() 实现上图这样的流程,写得是很爽,但过一段时间再看代码的时候,往往会遇到这个大问题:

我订阅的这个事件时谁丢出来的?

因为 .emit().done() 往往藏在 handler 里那一堆代码中,并不能一眼就找出来。

能否一开始就「声明」这个 handler 最后会丢出什么事件呢?(假如,约定一个 handler 只能丢 error 或它自己的事件)

new EventProxy()
.all('start', function authorize (done) {
  // ...
  fetchCredentical(done(function (res, json) {
    return json
  }))
})
.all('authorize', function profile (authorized, done) {
  fetchProfile(authorized.id, done)
})
.all('authorize', 'profile', function update (authorized, profile, done) {
  db.findAndUpdate(authorized.id, profile, done)
})
// ...

具名函数只是我设想的一种方式,利用函数名作为这个 handler 最后会触发的事件名,并将这个事件名隐含在 done 中。(只是举个_栗子_,并不是说想这样改造 .all() 嗯。。。)

或许会有更好的办法解决这个问题?

顺带问一下

朴老师有工具可以分析对 EP 的调用后生成类似上面这样的流程图嘛?

内存回收问题

你好,

如果我监听了某个事件, 如ep.on('evt',function(){}),那么这里的ep所占用的内存是如何回收的?removeAllListeners是不是可以解除所有的事件引用? 谢谢

问题

ep.emit("msg", "first"); ep.emit("msg", "second"); ep.emit("msg", "third");

这样能不能保证执行顺序呢?

done(event, fn):fn不能取得参数值

附上test/test.js代码:321行开始
it('done(event, fn)', function (done) {
var ep = EventProxy.create();
ep.bind('event1', function (data) {
should.exist(data);
assert.deepEqual(data, 'hehe', 'data should be modified');
done();
});
ep.bind('error', done);
fs.readFile(__filename, ep.done('event1', function (data) {
//不能取到data的值
return 'hehe';
}));
});

Bagpipe有可能被并过来用于实现类似async中的limit吗?

当我们需要从远程服务器获取资源时,高并发效率固然高,但并发量过多时,服务器肯定会有问题,就需要控制并发数量了。
目前API里并没有控制并发数量的方法,
在 issue #70 中,说配合bagpipe使用,那么你们肯定想过这个问题
https://github.com/JacksonTian/bagpipe

有一点不明白,控制并发数量也应该是比较常见的场景吧,那么为何不直接提供这个方法呢?(又可以省去一条依赖了,哈哈😄),把bagpipe分离出来又是何用意呢?或者是Bagpipe有可能被并过来用于实现类似async中的limit吗?请指点!

Error: Can't set headers after they are sent

场景
一个ajax post数据的验证的功能。

$.ajax({
  type: "POST",
  url: url,
  data: $("#sendForm").serialize(),
    success: function(data) {
  },
})

后端接收代码:
后端代码

第一次点击post后验证是正常返回的,后面在重复点击就会报错:
服务器响应: 500 Can't set headers after they are sent.

用的eventproxy做的流程控制,换成直接res.send()就好了,不知道是不是eventproxy的问题。

如何实现async中的limit?

看了下API貌似没有async的limit这样get的时候很容易就会被对方站给ban了请问如何实现用eventproxy实现limit呢?

据说提议 API 变更是作死的节奏。。。

和事件打交道用动词和名词作为 API 方法名的话,总感觉怪怪的。。

all

.all() 其实也是多个 .once() 嘛?合并作为 .once() 的重载?

ep.once('got template', 'fetched data', function (template, data) {
  // 毫无违和感。。。
});

tail

.tail() 赶脚和 .on() 是同一个东西。或者改叫 .whenever()

ep.whenever('compiled', 'refreshed', function (template, data) {
  // 。。。
})

对了,事件名是动词过去式。


纯粹洁癖,朴大大别打我。。。

bower安装出问题

我是使用bower管理第三方 lib 的,当然也包括 eventproxy

bower 安装过程中,安装不了最新版本(#0.3.2),依然还是#0.3.1,不知您是否能处理

关于trigger的一个疑问

trigger

请问一下这里的both总是会让while循环两次。第一次ev为参数eventName,第二次则为参数all。如果第一次触发了名为eventName的event,第二次又为什么要出发all?注释不是说:第一个参数如果传true才会listen all么?

How to call ep.after several times ?

When the callback of ep.after is returned, I want to use ep.after function once more. Do I need to create a new ep object ? or I just can use it directly ?

Thank you

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.