Commit 8cf231dd authored by GanJc's avatar GanJc

user api

parent 5fa006c7
......@@ -18,7 +18,7 @@ app.set('view engine', 'jade');
//allow custom header and CORS
app.all('*',function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
// res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
if (req.method == 'OPTIONS') {
res.send(200);
......
EventProxy [![Build Status](https://secure.travis-ci.org/JacksonTian/eventproxy.png)](http://travis-ci.org/JacksonTian/eventproxy) [![NPM version](https://badge.fury.io/js/eventproxy.png)](http://badge.fury.io/js/eventproxy) [![spm package](http://spmjs.io/badge/eventproxy)](http://spmjs.io/package/eventproxy) [English Doc](https://github.com/JacksonTian/eventproxy/blob/master/README_en.md)
======
[![NPM](https://nodei.co/npm/eventproxy.png?downloads=true&stars=true)](https://nodei.co/npm/eventproxy)
> 这个世界上不存在所谓回调函数深度嵌套的问题。 —— [Jackson Tian](http://weibo.com/shyvo)
> 世界上本没有嵌套回调,写得人多了,也便有了`}}}}}}}}}}}}`。 —— [fengmk2](http://fengmk2.github.com)
* API文档: [API Documentation](http://html5ify.com/eventproxy/api.html)
* jscoverage: [97%](http://html5ify.com/eventproxy/coverage.html)
* 源码注解:[注解文档](http://html5ify.com/eventproxy/eventproxy.html)
EventProxy 仅仅是一个很轻量的工具,但是能够带来一种事件式编程的思维变化。有几个特点:
1. 利用事件机制解耦复杂业务逻辑
2. 移除被广为诟病的深度callback嵌套问题
3. 将串行等待变成并行等待,提升多异步协作场景下的执行效率
4. 友好的Error handling
5. 无平台依赖,适合前后端,能用于浏览器和Node.js
6. 兼容CMD,AMD以及CommonJS模块环境
现在的,无深度嵌套的,并行的
```js
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);
});
```
过去的,深度嵌套的,串行的。
```js
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安装即可使用:
```bash
$ npm install eventproxy
```
调用:
```js
var EventProxy = require('eventproxy');
```
### [spm](http://spmjs.io/package/eventproxy)
```bash
$ spm install eventproxy
```
### Component
```bash
$ component install JacksonTian/eventproxy
```
### 前端用户
以下示例均指向Github的源文件地址,您也可以[下载源文件](https://raw.github.com/JacksonTian/eventproxy/master/lib/eventproxy.js)到你自己的项目中。整个文件注释全面,带注释和空行,一共约500行。为保证EventProxy的易嵌入,项目暂不提供压缩版。用户可以自行采用Uglify、YUI Compressor或Google Closure Complier进行压缩。
#### 普通环境
在页面中嵌入脚本即可使用:
```html
<script src="https://raw.github.com/JacksonTian/eventproxy/master/lib/eventproxy.js"></script>
```
使用:
```js
// EventProxy此时是一个全局变量
var ep = new EventProxy();
```
#### SeaJS用户
SeaJS下只需配置别名,然后`require`引用即可使用。
```js
// 配置
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规范。
```js
// 配置路径
require.config({
paths: {
eventproxy: "https://raw.github.com/JacksonTian/eventproxy/master/lib/eventproxy"
}
});
// 使用
require(["eventproxy"], function (EventProxy) {
// TODO
});
```
## 异步协作
### 多类型异步协作
此处以页面渲染为场景,渲染页面需要模板、数据。假设都需要异步读取。
```js
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`事件。
```js
var ep = EventProxy.create('tpl', 'data', function (tpl, data) {
// TODO
});
```
以上方法等效于
```js
var ep = new EventProxy();
ep.all('tpl', 'data', function (tpl, data) {
// TODO
});
```
### 重复异步协作
此处以读取目录下的所有文件为例,在异步操作中,我们需要在所有异步调用结束后,执行某些操作。
```js
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将会被调用执行,每次触发的数据,将会按触发顺序,存为数组作为参数传入。
### 持续型异步协作
此处以股票为例,数据和模板都是异步获取,但是数据会持续刷新,视图会需要重新刷新。
```js
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);
```
`tail``all`方法比较类似,都是注册到事件组合上。不同在于,指定事件都触发之后,如果事件依旧持续触发,将会在每次触发时调用handler,极像一条尾巴。
## 基本事件
通过事件实现异步协作是EventProxy的主要亮点。除此之外,它还是一个基本的事件库。携带如下基本API
- `on`/`addListener`,绑定事件监听器
- `emit`,触发事件
- `once`,绑定只执行一次的事件监听器
- `removeListener`,移除事件的监听器
- `removeAllListeners`,移除单个事件或者所有事件的监听器
为了照顾各个环境的开发者,上面的方法多具有别名。
- YUI3使用者,`subscribe``fire`你应该知道分别对应的是`on`/`addListener``emit`
- jQuery使用者,`trigger`对应的方法是`emit``bind`对应的就是`on`/`addListener`
- `removeListener``removeAllListeners`其实都可以通过别名`unbind`完成。
所以在你的环境下,选用你喜欢的API即可。
更多API的描述请访问[API Docs](http://html5ify.com/eventproxy/api.html)
## 异常处理
在异步方法中,实际上,异常处理需要占用一定比例的精力。在过去一段时间内,我们都是通过额外添加`error`事件来进行处理的,代码大致如下:
```js
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经过很多实践后,我们根据我们的最佳实践提供了优化的错误处理方案。
```js
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
```js
ep.fail(callback);
// 由于参数位相同,它实际是
ep.fail(function (err) {
callback(err);
});
// 等价于
ep.bind('error', function (err) {
// 卸载掉所有handler
ep.unbind();
// 异常回调
callback(err);
});
```
`fail`方法侦听了`error`事件,默认处理卸载掉所有handler,并调用回调函数。
### 神奇的 throw
`throw``ep.emit('error', err)` 的简写。
```js
var err = new Error();
ep.throw(err);
// 实际是
ep.emit('error', err);
```
### 神奇的done
```js
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`)后剩余的参数,传递给该回调函数作为参数。该回调函数无需考虑异常处理。
```js
ep.done(function (content) {
// 这里无需考虑异常
// 手工emit
ep.emit('someevent', newcontent);
});
```
当然手工emit的方式并不太好,我们更进一步的版本:
```js
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`方法。
```js
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`函数的设计,它包含异常的传递。同时它还隐含了对返回数据进行编号,在结束时,按顺序返回。
```js
ep.group('got_file');
// 约等价于
function (err, data) {
if (err) {
return ep.emit('error', err);
}
ep.emit('got_file', data);
};
```
当回调函数的数据还需要进行加工时,可以给`group`带上回调函数,只要在操作后将数据返回即可:
```js
ep.group('got_file', function (data) {
// some code
return data;
});
```
### 异步事件触发: emitLater && doneLater
在node中,`emit`方法是同步的,EventProxy中的`emit``trigger`等跟node的风格一致,也是同步的。看下面这段代码,可能眼尖的同学一下就发现了隐藏的bug:
```js
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.check``callback`被同步执行了,在`ep`监听`check`事件之前,它就已经被抛出来了,后续逻辑没办法继续执行。尽管node的约定是所有的`callback`都是需要异步返回的,但是如果这个方法是由第三方提供的,我们没有办法保证`db.check``callback`一定会异步执行,所以我们的代码通常就变成了这样:
```js
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.check``check`->`get`->`render`的逻辑,在代码中看起来变成了`get`->`render`->`check`。如果整个逻辑更加复杂,这种风格将会让代码很难读懂。
这时候,我们需要的就是 __异步事件触发__:
```js
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`来解决:
```js
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回馈良多。
```bash
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](https://github.com/JacksonTian/eventproxy/blob/master/MIT-License)。请自由享受开源。
## 捐赠
如果您觉得本模块对您有帮助,欢迎请作者一杯咖啡
![捐赠EventProxy](https://cloud.githubusercontent.com/assets/327019/2941591/2b9e5e58-d9a7-11e3-9e80-c25aba0a48a1.png)
或者[![](http://img.shields.io/gratipay/JacksonTian.svg)](https://www.gittip.com/JacksonTian/)
module.exports = require('./lib/eventproxy');
/*global define*/
!(function (name, definition) {
// Check define
var hasDefine = typeof define === 'function',
// Check exports
hasExports = typeof module !== 'undefined' && module.exports;
if (hasDefine) {
// AMD Module or CMD Module
define('eventproxy_debug', function () {return function () {};});
define(['eventproxy_debug'], definition);
} else if (hasExports) {
// Node.js Module
module.exports = definition(require('debug')('eventproxy'));
} else {
// Assign to common namespaces or simply the global object (window)
this[name] = definition();
}
})('EventProxy', function (debug) {
debug = debug || function () {};
/*!
* refs
*/
var SLICE = Array.prototype.slice;
var CONCAT = Array.prototype.concat;
var ALL_EVENT = '__all__';
/**
* EventProxy. An implementation of task/event based asynchronous pattern.
* A module that can be mixed in to *any object* in order to provide it with custom events.
* You may `bind` or `unbind` a callback function to an event;
* `trigger`-ing an event fires all callbacks in succession.
* Examples:
* ```js
* var render = function (template, resources) {};
* var proxy = new EventProxy();
* proxy.assign("template", "l10n", render);
* proxy.trigger("template", template);
* proxy.trigger("l10n", resources);
* ```
*/
var EventProxy = function () {
if (!(this instanceof EventProxy)) {
return new EventProxy();
}
this._callbacks = {};
this._fired = {};
};
/**
* Bind an event, specified by a string name, `ev`, to a `callback` function.
* Passing __ALL_EVENT__ will bind the callback to all events fired.
* Examples:
* ```js
* var proxy = new EventProxy();
* proxy.addListener("template", function (event) {
* // TODO
* });
* ```
* @param {String} eventname Event name.
* @param {Function} callback Callback.
*/
EventProxy.prototype.addListener = function (ev, callback) {
debug('Add listener for %s', ev);
this._callbacks[ev] = this._callbacks[ev] || [];
this._callbacks[ev].push(callback);
return this;
};
/**
* `addListener` alias, `bind`
*/
EventProxy.prototype.bind = EventProxy.prototype.addListener;
/**
* `addListener` alias, `on`
*/
EventProxy.prototype.on = EventProxy.prototype.addListener;
/**
* `addListener` alias, `subscribe`
*/
EventProxy.prototype.subscribe = EventProxy.prototype.addListener;
/**
* Bind an event, but put the callback into head of all callbacks.
* @param {String} eventname Event name.
* @param {Function} callback Callback.
*/
EventProxy.prototype.headbind = function (ev, callback) {
debug('Add listener for %s', ev);
this._callbacks[ev] = this._callbacks[ev] || [];
this._callbacks[ev].unshift(callback);
return this;
};
/**
* Remove one or many callbacks.
*
* - If `callback` is null, removes all callbacks for the event.
* - If `eventname` is null, removes all bound callbacks for all events.
* @param {String} eventname Event name.
* @param {Function} callback Callback.
*/
EventProxy.prototype.removeListener = function (eventname, callback) {
var calls = this._callbacks;
if (!eventname) {
debug('Remove all listeners');
this._callbacks = {};
} else {
if (!callback) {
debug('Remove all listeners of %s', eventname);
calls[eventname] = [];
} else {
var list = calls[eventname];
if (list) {
var l = list.length;
for (var i = 0; i < l; i++) {
if (callback === list[i]) {
debug('Remove a listener of %s', eventname);
list[i] = null;
}
}
}
}
}
return this;
};
/**
* `removeListener` alias, unbind
*/
EventProxy.prototype.unbind = EventProxy.prototype.removeListener;
/**
* Remove all listeners. It equals unbind()
* Just add this API for as same as Event.Emitter.
* @param {String} event Event name.
*/
EventProxy.prototype.removeAllListeners = function (event) {
return this.unbind(event);
};
/**
* Bind the ALL_EVENT event
*/
EventProxy.prototype.bindForAll = function (callback) {
this.bind(ALL_EVENT, callback);
};
/**
* Unbind the ALL_EVENT event
*/
EventProxy.prototype.unbindForAll = function (callback) {
this.unbind(ALL_EVENT, callback);
};
/**
* Trigger an event, firing all bound callbacks. Callbacks are passed the
* same arguments as `trigger` is, apart from the event name.
* Listening for `"all"` passes the true event name as the first argument.
* @param {String} eventname Event name
* @param {Mix} data Pass in data
*/
EventProxy.prototype.trigger = function (eventname, data) {
var list, ev, callback, i, l;
var both = 2;
var calls = this._callbacks;
debug('Emit event %s with data %j', eventname, data);
while (both--) {
ev = both ? eventname : ALL_EVENT;
list = calls[ev];
if (list) {
for (i = 0, l = list.length; i < l; i++) {
if (!(callback = list[i])) {
list.splice(i, 1);
i--;
l--;
} else {
var args = [];
var start = both ? 1 : 0;
for (var j = start; j < arguments.length; j++) {
args.push(arguments[j]);
}
callback.apply(this, args);
}
}
}
}
return this;
};
/**
* `trigger` alias
*/
EventProxy.prototype.emit = EventProxy.prototype.trigger;
/**
* `trigger` alias
*/
EventProxy.prototype.fire = EventProxy.prototype.trigger;
/**
* Bind an event like the bind method, but will remove the listener after it was fired.
* @param {String} ev Event name
* @param {Function} callback Callback
*/
EventProxy.prototype.once = function (ev, callback) {
var self = this;
var wrapper = function () {
callback.apply(self, arguments);
self.unbind(ev, wrapper);
};
this.bind(ev, wrapper);
return this;
};
var later = (typeof setImmediate !== 'undefined' && setImmediate) ||
(typeof process !== 'undefined' && process.nextTick) || function (fn) {
setTimeout(fn, 0);
};
/**
* emitLater
* make emit async
*/
EventProxy.prototype.emitLater = function () {
var self = this;
var args = arguments;
later(function () {
self.trigger.apply(self, args);
});
};
/**
* Bind an event, and trigger it immediately.
* @param {String} ev Event name.
* @param {Function} callback Callback.
* @param {Mix} data The data that will be passed to calback as arguments.
*/
EventProxy.prototype.immediate = function (ev, callback, data) {
this.bind(ev, callback);
this.trigger(ev, data);
return this;
};
/**
* `immediate` alias
*/
EventProxy.prototype.asap = EventProxy.prototype.immediate;
var _assign = function (eventname1, eventname2, cb, once) {
var proxy = this;
var argsLength = arguments.length;
var times = 0;
var flag = {};
// Check the arguments length.
if (argsLength < 3) {
return this;
}
var events = SLICE.call(arguments, 0, -2);
var callback = arguments[argsLength - 2];
var isOnce = arguments[argsLength - 1];
// Check the callback type.
if (typeof callback !== "function") {
return this;
}
debug('Assign listener for events %j, once is %s', events, !!isOnce);
var bind = function (key) {
var method = isOnce ? "once" : "bind";
proxy[method](key, function (data) {
proxy._fired[key] = proxy._fired[key] || {};
proxy._fired[key].data = data;
if (!flag[key]) {
flag[key] = true;
times++;
}
});
};
var length = events.length;
for (var index = 0; index < length; index++) {
bind(events[index]);
}
var _all = function (event) {
if (times < length) {
return;
}
if (!flag[event]) {
return;
}
var data = [];
for (var index = 0; index < length; index++) {
data.push(proxy._fired[events[index]].data);
}
if (isOnce) {
proxy.unbindForAll(_all);
}
debug('Events %j all emited with data %j', events, data);
callback.apply(null, data);
};
proxy.bindForAll(_all);
};
/**
* Assign some events, after all events were fired, the callback will be executed once.
*
* Examples:
* ```js
* proxy.all(ev1, ev2, callback);
* proxy.all([ev1, ev2], callback);
* proxy.all(ev1, [ev2, ev3], callback);
* ```
* @param {String} eventname1 First event name.
* @param {String} eventname2 Second event name.
* @param {Function} callback Callback, that will be called after predefined events were fired.
*/
EventProxy.prototype.all = function (eventname1, eventname2, callback) {
var args = CONCAT.apply([], arguments);
args.push(true);
_assign.apply(this, args);
return this;
};
/**
* `all` alias
*/
EventProxy.prototype.assign = EventProxy.prototype.all;
/**
* Assign the only one 'error' event handler.
* @param {Function(err)} callback
*/
EventProxy.prototype.fail = function (callback) {
var that = this;
that.once('error', function () {
that.unbind();
// put all arguments to the error handler
// fail(function(err, args1, args2, ...){})
callback.apply(null, arguments);
});
return this;
};
/**
* A shortcut of ep#emit('error', err)
*/
EventProxy.prototype.throw = function () {
var that = this;
that.emit.apply(that, ['error'].concat(SLICE.call(arguments)));
};
/**
* Assign some events, after all events were fired, the callback will be executed first time.
* Then any event that predefined be fired again, the callback will executed with the newest data.
* Examples:
* ```js
* proxy.tail(ev1, ev2, callback);
* proxy.tail([ev1, ev2], callback);
* proxy.tail(ev1, [ev2, ev3], callback);
* ```
* @param {String} eventname1 First event name.
* @param {String} eventname2 Second event name.
* @param {Function} callback Callback, that will be called after predefined events were fired.
*/
EventProxy.prototype.tail = function () {
var args = CONCAT.apply([], arguments);
args.push(false);
_assign.apply(this, args);
return this;
};
/**
* `tail` alias, assignAll
*/
EventProxy.prototype.assignAll = EventProxy.prototype.tail;
/**
* `tail` alias, assignAlways
*/
EventProxy.prototype.assignAlways = EventProxy.prototype.tail;
/**
* The callback will be executed after the event be fired N times.
* @param {String} eventname Event name.
* @param {Number} times N times.
* @param {Function} callback Callback, that will be called after event was fired N times.
*/
EventProxy.prototype.after = function (eventname, times, callback) {
if (times === 0) {
callback.call(null, []);
return this;
}
var proxy = this,
firedData = [];
this._after = this._after || {};
var group = eventname + '_group';
this._after[group] = {
index: 0,
results: []
};
debug('After emit %s times, event %s\'s listenner will execute', times, eventname);
var all = function (name, data) {
if (name === eventname) {
times--;
firedData.push(data);
if (times < 1) {
debug('Event %s was emit %s, and execute the listenner', eventname, times);
proxy.unbindForAll(all);
callback.apply(null, [firedData]);
}
}
if (name === group) {
times--;
proxy._after[group].results[data.index] = data.result;
if (times < 1) {
debug('Event %s was emit %s, and execute the listenner', eventname, times);
proxy.unbindForAll(all);
callback.call(null, proxy._after[group].results);
}
}
};
proxy.bindForAll(all);
return this;
};
/**
* The `after` method's helper. Use it will return ordered results.
* If you need manipulate result, you need callback
* Examples:
* ```js
* var ep = new EventProxy();
* ep.after('file', files.length, function (list) {
* // Ordered results
* });
* for (var i = 0; i < files.length; i++) {
* fs.readFile(files[i], 'utf-8', ep.group('file'));
* }
* ```
* @param {String} eventname Event name, shoule keep consistent with `after`.
* @param {Function} callback Callback function, should return the final result.
*/
EventProxy.prototype.group = function (eventname, callback) {
var that = this;
var group = eventname + '_group';
var index = that._after[group].index;
that._after[group].index++;
return function (err, data) {
if (err) {
// put all arguments to the error handler
return that.emit.apply(that, ['error'].concat(SLICE.call(arguments)));
}
that.emit(group, {
index: index,
// callback(err, args1, args2, ...)
result: callback ? callback.apply(null, SLICE.call(arguments, 1)) : data
});
};
};
/**
* The callback will be executed after any registered event was fired. It only executed once.
* @param {String} eventname1 Event name.
* @param {String} eventname2 Event name.
* @param {Function} callback The callback will get a map that has data and eventname attributes.
*/
EventProxy.prototype.any = function () {
var proxy = this,
callback = arguments[arguments.length - 1],
events = SLICE.call(arguments, 0, -1),
_eventname = events.join("_");
debug('Add listenner for Any of events %j emit', events);
proxy.once(_eventname, callback);
var _bind = function (key) {
proxy.bind(key, function (data) {
debug('One of events %j emited, execute the listenner');
proxy.trigger(_eventname, {"data": data, eventName: key});
});
};
for (var index = 0; index < events.length; index++) {
_bind(events[index]);
}
};
/**
* The callback will be executed when the event name not equals with assigned event.
* @param {String} eventname Event name.
* @param {Function} callback Callback.
*/
EventProxy.prototype.not = function (eventname, callback) {
var proxy = this;
debug('Add listenner for not event %s', eventname);
proxy.bindForAll(function (name, data) {
if (name !== eventname) {
debug('listenner execute of event %s emit, but not event %s.', name, eventname);
callback(data);
}
});
};
/**
* Success callback wrapper, will handler err for you.
*
* ```js
* fs.readFile('foo.txt', ep.done('content'));
*
* // equal to =>
*
* fs.readFile('foo.txt', function (err, content) {
* if (err) {
* return ep.emit('error', err);
* }
* ep.emit('content', content);
* });
* ```
*
* ```js
* fs.readFile('foo.txt', ep.done('content', function (content) {
* return content.trim();
* }));
*
* // equal to =>
*
* fs.readFile('foo.txt', function (err, content) {
* if (err) {
* return ep.emit('error', err);
* }
* ep.emit('content', content.trim());
* });
* ```
* @param {Function|String} handler, success callback or event name will be emit after callback.
* @return {Function}
*/
EventProxy.prototype.done = function (handler, callback) {
var that = this;
return function (err, data) {
if (err) {
// put all arguments to the error handler
return that.emit.apply(that, ['error'].concat(SLICE.call(arguments)));
}
// callback(err, args1, args2, ...)
var args = SLICE.call(arguments, 1);
if (typeof handler === 'string') {
// getAsync(query, ep.done('query'));
// or
// getAsync(query, ep.done('query', function (data) {
// return data.trim();
// }));
if (callback) {
// only replace the args when it really return a result
return that.emit(handler, callback.apply(null, args));
} else {
// put all arguments to the done handler
//ep.done('some');
//ep.on('some', function(args1, args2, ...){});
return that.emit.apply(that, [handler].concat(args));
}
}
// speed improve for mostly case: `callback(err, data)`
if (arguments.length <= 2) {
return handler(data);
}
// callback(err, args1, args2, ...)
handler.apply(null, args);
};
};
/**
* make done async
* @return {Function} delay done
*/
EventProxy.prototype.doneLater = function (handler, callback) {
var _doneHandler = this.done(handler, callback);
return function (err, data) {
var args = arguments;
later(function () {
_doneHandler.apply(null, args);
});
};
};
/**
* Create a new EventProxy
* Examples:
* ```js
* var ep = EventProxy.create();
* ep.assign('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...
* });
* ```
* @return {EventProxy} EventProxy instance
*/
EventProxy.create = function () {
var ep = new EventProxy();
var args = CONCAT.apply([], arguments);
if (args.length) {
var errorHandler = args[args.length - 1];
var callback = args[args.length - 2];
if (typeof errorHandler === 'function' && typeof callback === 'function') {
args.pop();
ep.fail(errorHandler);
}
ep.assign.apply(ep, args);
}
return ep;
};
// Backwards compatibility
EventProxy.EventProxy = EventProxy;
return EventProxy;
});
{
"_args": [
[
"eventproxy",
"/Users/linus/learn/ygo/ygopro-arena-api"
]
],
"_cnpm_publish_time": 1468983521805,
"_from": "eventproxy@latest",
"_id": "eventproxy@0.3.5",
"_inCache": true,
"_installable": true,
"_location": "/eventproxy",
"_nodeVersion": "4.4.4",
"_npmOperationalInternal": {
"host": "packages-12-west.internal.npmjs.com",
"tmp": "tmp/eventproxy-0.3.5.tgz_1468983520153_0.16356190340593457"
},
"_npmUser": {
"email": "shyvo1987@gmail.com",
"name": "jacksontian"
},
"_npmVersion": "2.15.1",
"_phantomChildren": {},
"_requested": {
"name": "eventproxy",
"raw": "eventproxy",
"rawSpec": "",
"scope": null,
"spec": "latest",
"type": "tag"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npm.taobao.org/eventproxy/download/eventproxy-0.3.5.tgz",
"_shasum": "4db3290dcbc51cf067cbb6752e3c40b5d917212f",
"_shrinkwrap": null,
"_spec": "eventproxy",
"_where": "/Users/linus/learn/ygo/ygopro-arena-api",
"author": {
"email": "shyvo1987@gmail.com",
"name": "Jackson Tian"
},
"bugs": {
"url": "https://github.com/JacksonTian/eventproxy/issues"
},
"contributors": [
{
"email": "shyvo1987@gmail.com",
"name": "Jackson Tian"
},
{
"email": "fengmk2@gmail.com",
"name": "fengmk2"
}
],
"dependencies": {
"debug": "2.2.0"
},
"description": "An implementation of task/event based asynchronous pattern.",
"devDependencies": {
"browser": "0.2.6",
"contributors": "*",
"growl": "1.9.2",
"istanbul": "*",
"mocha": "2.5.3",
"pedding": "1.0.0"
},
"directories": {
"test": "test"
},
"dist": {
"noattachment": false,
"shasum": "4db3290dcbc51cf067cbb6752e3c40b5d917212f",
"size": 10722,
"tarball": "http://registry.npm.taobao.org/eventproxy/download/eventproxy-0.3.5.tgz"
},
"files": [
"lib",
"index.js"
],
"gitHead": "410b8a2021be9e1ed6ae396483bfb2ebe34f223e",
"homepage": "https://github.com/JacksonTian/eventproxy",
"keywords": [
"event",
"task-base",
"event machine",
"nested callback terminator"
],
"license": "MIT",
"main": "index.js",
"maintainers": [
{
"email": "shyvo1987@gmail.com",
"name": "jacksontian"
}
],
"name": "eventproxy",
"optionalDependencies": {},
"publish_time": 1468983521805,
"readme": "ERROR: No README data found!",
"repository": {
"type": "git",
"url": "git://github.com/JacksonTian/eventproxy.git"
},
"scripts": {
"test": "make test-all"
},
"spm": {
"ignore": [
"test"
],
"main": "index.js"
},
"version": "0.3.5"
}
......@@ -4,6 +4,7 @@
var express = require('express');
var router = express.Router();
var pg = require('pg');
var eventproxy = require('eventproxy');
// create a config to configure both pooling behavior
// and client options
......@@ -66,4 +67,97 @@ router.get('/users', function (req, res) {
})
});
router.get('/user', function (req, res) {
// to run a query we can acquire a client from the pool,
// run a query on the client, and then return the client to the pool
pool.connect(function (err, client, done) {
if (err) {
return console.error('error fetching client from pool', err);
}
var username = req.query.username ;
if(!username){
res.json({message: "error, username require"});
return
}
var resultData = {
'exp': '800',
'exp_rank': '0',
'pt': '800',
'arena_rank': '0',
'win': '0',
'lose': '0',
'draw': '0',
'all': '0',
'ratio': '0'
}
var sql = `SELECT * from rating_index where username = '${username}'`
console.log(sql);
client.query(sql, function (err, result) {
//call `done()` to release the client back to the pool
done();
if (err) {
return console.error('error running query', err);
}
if(result.rows.length === 0){
res.json({message: "error, user not found"});
}else {
var user = result.rows[0]
resultData['exp'] = user['exp']
resultData['pt'] = user['pt']
resultData['win'] = user['win']
resultData['lose'] = user['lose']
resultData['all'] = user['game']
resultData['draw'] = user['game'] - user['lose'] - user['win']
resultData['ratio'] = (user['win'] / user['game'] * 100 ).toFixed(2)
var ep = new eventproxy();
ep.after('delay', 2, function (row) {
console.log('final:');
console.log(resultData);
res.json(resultData);
});
client.query(`SELECT count(*) from rating_index where pt >= ${resultData['pt']}`, function (err, result) {
done();
if (err) {
return console.error('error running query', err);
}
resultData['arena_rank'] = result.rows[0]['count']
ep.emit('delay', '');
});
client.query(`SELECT count(*) from rating_index where exp >= ${resultData['exp']}`, function (err, result) {
done();
if (err) {
return console.error('error running query', err);
}
resultData['exp_rank'] = result.rows[0]['count']
ep.emit('delay', '');
});
}
});
});
pool.on('error', function (err, client) {
// if an error is encountered by a client while it sits idle in the pool
// the pool itself will emit an error event with both the error and
// the client which emitted the original error
// this is a rare occurrence but can happen if there is a network partition
// between your application and the database, the database restarts, etc.
// and so you might want to handle it and at least log it out
console.error('idle client error', err.message, err.stack)
res.json({message: "error"});
})
});
module.exports = router;
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment