安裝
npm install eventproxy --save
調用
var EventProxy = require('eventproxy');
異步協作
多類型異步協作
此處以頁面渲染為場景,渲染頁面需要模版、數據。假設都需要異步讀取。
var EventProxy = require('eventproxy');
//獲取EventProxy實例
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('sql', function(err, content){
ep.emit('data', result);
});
all
方法將handler注冊到事件組合上。當注冊的所有事件均觸發后,將會調用handler執行,每個事件傳遞的數據,將會依照事件名順序,傳入handler作為參數。
快速創建
EventProxy提供了create
靜態方法,可以快速完成注冊all事件。
var ep_create = 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個網站,讀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('sql', function(err, result){
ep.emit('data', result);
});
}, 2000);
tail
與all
方法比較類似, 都是注冊到事件組合上。不同在於,指定事件都觸發之后,如果事件依舊持續觸發,將會在每次觸發時調用handler,像一條尾巴一樣。
基本事件
通過事件實現異步協作是EventProxy的主要亮點。除此之外,它還是一個基本的事件庫。攜帶如下基本API
on/addListener
綁定事件監聽器emit
觸發事件once
綁定只執行一次的事件監聽器removeListener
移除事件監聽器removeAllListeners
移除單個事件或所有事件的監聽器
異常處理
在異步方法中,實際上,異常處理需要占用一定比例的經歷。在過去一段時間內,我們都是通過額外添加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('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('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方法
throw
是ep.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('event', content);
});
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){
return data;
});
注意事項
- 請勿使用
all
作為業務的事件名。該事件名為保留事件。 - 異常處理部分,請遵循Node的最佳實踐(回調函數首個參數為異常傳遞位)。
上述內容為學習筆記,選自https://github.com/JacksonTian/eventproxy
歡迎轉載,轉載請注明出處
update by 2017/7/25 15:02
該部分完結
by 一枝豬