1. PubSub事件
事件監(jiān)聽模式是一種廣泛應(yīng)用于異步編程的解決方案铣缠,是回調(diào)函數(shù)的事件化,又稱發(fā)布訂閱模式。
關(guān)于發(fā)布訂閱的實(shí)現(xiàn)蝗蛙,可以看我之前的另外一篇文章聊聊設(shè)計(jì)模式(1):發(fā)布訂閱模式
Node中的發(fā)布訂閱模式
Node自身提供的events
模塊是發(fā)布訂閱的一個(gè)基本實(shí)現(xiàn)蝇庭,相較于瀏覽器的事件機(jī)制,它更為簡(jiǎn)單:不存在事件冒泡捡硅,也不存在preventDefault()
,stopImmediatePropagation
等控制事件傳遞的方法遗契。它具有emit
,on
,removeListener
,once
,removeAllListener
等方法。
訂閱事件on
就是一個(gè)高階函數(shù),發(fā)布訂閱模式可以實(shí)現(xiàn)一個(gè)事件與多個(gè)回調(diào)函數(shù)之間的關(guān)聯(lián)病曾,這些回調(diào)函數(shù)又可以稱為事件偵聽器牍蜂。emit
發(fā)布事件后,消息會(huì)傳給當(dāng)前函數(shù)的所有偵聽器執(zhí)行泰涂。
發(fā)布訂閱模式可用于解耦業(yè)務(wù)邏輯鲫竞,將不變的內(nèi)容封裝在組件內(nèi)部,將容易變化的暴露給外部處理逼蒙。另一個(gè)角度看从绘,發(fā)布訂閱模式也是一種鉤子(hock)
的機(jī)制,利用鉤子導(dǎo)出內(nèi)部數(shù)據(jù)或者狀態(tài)給外部調(diào)用者是牢。node中很多對(duì)象大多具有黑盒的效用僵井,如果我們不通過鉤子的形式,很難獲取對(duì)象在運(yùn)行期間的中間值或者內(nèi)部狀態(tài)驳棱∨玻可以使編程者不再關(guān)注組件是如何啟動(dòng)的,只需關(guān)注在事件點(diǎn)上社搅。HTTP
請(qǐng)求便是常見的應(yīng)用場(chǎng)景驻债。
var options = {
host: 'www.google.com',
port: 80,
path: '/upload',
method: 'POST'
};
var req = http.request(options, function (res) {
console.log('STATUS: ' + res.statusCode);
console.log('HEADERS: ' + JSON.stringify(res.headers));
res.setEncoding('utf8');
res.on('data', function (chunk) {
console.log('BODY: ' + chunk);
});
});
req.on('error', function (e) {
console.log('problem with request: ' + e.message);
});
// write data to request body
req.write('data\n');
req.write('data\n');
req.end();
在這段HTTP request的代碼中,程序員只需要將視線放在error形葬,data這些業(yè)務(wù)事件點(diǎn)即可合呐,至于內(nèi)部的流程如何,無(wú)需過于關(guān)注笙以。
Node為發(fā)布訂閱模式做了特殊的優(yōu)化淌实,下面為具體兩個(gè)細(xì)節(jié)點(diǎn):
- 如果一個(gè)事件添加了超過十個(gè)監(jiān)聽器,將會(huì)收到一個(gè)警告猖腕,調(diào)用
emmiter.setMaxListener(0)
可以將限制去掉 - 為了異常處理
EventEmmiter
對(duì)象對(duì)error
事件做了特殊處理拆祈,如果觸發(fā)了error
事件,會(huì)檢查是否對(duì)error
事件添加了事件監(jiān)聽谈息。如果添加了缘屹,就交給該偵聽器,否則這個(gè)錯(cuò)誤會(huì)作為異常拋出侠仇。如果外部沒有捕獲這個(gè)異常,將會(huì),將會(huì)引起線程退出逻炊。
繼承events模塊
var events = require('events');
function Stream(){
events.EventEmitter.call(this);
}
util.inherits(Stream, events.EventEmitter)
事件隊(duì)列解決雪崩
利用once()
方法互亮,將所有回調(diào)都?jí)喝胧录?duì)列,利用只執(zhí)行一次就會(huì)將監(jiān)視移除余素,保證每一個(gè)回調(diào)只執(zhí)行一次
多異步之間的協(xié)作
類似promise
中的race()
方法豹休,實(shí)現(xiàn)就是引入一個(gè)哨兵變量,利用偏函數(shù)處理哨兵變量和偏函數(shù)之間的關(guān)系桨吊。
2. Promise/Deffered模式
Promise 是異步編程的一種解決方案威根,比傳統(tǒng)的解決方案——回調(diào)函數(shù)和事件——更合理和更強(qiáng)大。它由社區(qū)最早提出和實(shí)現(xiàn)视乐,ES6 將其寫進(jìn)了語(yǔ)言標(biāo)準(zhǔn)洛搀,統(tǒng)一了用法,原生提供了Promise對(duì)象佑淀。
所謂Promise留美,簡(jiǎn)單說(shuō)就是一個(gè)容器,里面保存著某個(gè)未來(lái)才會(huì)結(jié)束的事件(通常是一個(gè)異步操作)的結(jié)果伸刃。從語(yǔ)法上說(shuō)谎砾,Promise 是一個(gè)對(duì)象,從它可以獲取異步操作的消息捧颅。Promise 提供統(tǒng)一的 API景图,各種異步操作都可以用同樣的方法進(jìn)行處理。
Promise對(duì)象有以下兩個(gè)特點(diǎn)碉哑。
(1)對(duì)象的狀態(tài)不受外界影響症歇。Promise對(duì)象代表一個(gè)異步操作,有三種狀態(tài):pending(進(jìn)行中)谭梗、fulfilled(已成功)和rejected(已失斖睢)。只有異步操作的結(jié)果激捏,可以決定當(dāng)前是哪一種狀態(tài)设塔,任何其他操作都無(wú)法改變這個(gè)狀態(tài)。這也是Promise
這個(gè)名字的由來(lái)远舅,它的英語(yǔ)意思就是“承諾”闰蛔,表示其他手段無(wú)法改變。
(2)一旦狀態(tài)改變图柏,就不會(huì)再變序六,任何時(shí)候都可以得到這個(gè)結(jié)果。Promise
對(duì)象的狀態(tài)改變蚤吹,只有兩種可能:從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected例诀。只要這兩種情況發(fā)生随抠,狀態(tài)就凝固了,不會(huì)再變了繁涂,會(huì)一直保持這個(gè)結(jié)果拱她,這時(shí)就稱為 resolved(已定型)。如果改變已經(jīng)發(fā)生了,你再Promise對(duì)象添加回調(diào)函數(shù),也會(huì)立即得到這個(gè)結(jié)果瞻润。這與事件(Event)完全不同永乌,事件的特點(diǎn)是,如果你錯(cuò)過了它,再去監(jiān)聽,是得不到結(jié)果的。
注意敞咧,為了行文方便,本章后面的resolved統(tǒng)一只指fulfilled狀態(tài)倔矾,不包含rejected狀態(tài)妄均。
有了Promise對(duì)象,就可以將異步操作以同步操作的流程表達(dá)出來(lái)哪自,避免了層層嵌套的回調(diào)函數(shù)丰包。此外,Promise
對(duì)象提供統(tǒng)一的接口壤巷,使得控制異步操作更加容易邑彪。
Promise也有一些缺點(diǎn)。首先胧华,無(wú)法取消Promise寄症,一旦新建它就會(huì)立即執(zhí)行,無(wú)法中途取消矩动。其次有巧,如果不設(shè)置回調(diào)函數(shù),Promise內(nèi)部拋出的錯(cuò)誤悲没,不會(huì)反應(yīng)到外部篮迎。第三,當(dāng)處于pending狀態(tài)時(shí)示姿,無(wú)法得知目前進(jìn)展到哪一個(gè)階段(剛剛開始還是即將完成)甜橱。
如果某些事件不斷地反復(fù)發(fā)生,一般來(lái)說(shuō)栈戳,使用 Stream 模式是比部署Promise
更好的選擇岂傲。
上面是阮一峰ES6標(biāo)準(zhǔn)教程中關(guān)于Promise
的定義,Promise/Deferred模式包含兩部分子檀,即是Promise
和Deferred
.
看了promise的介紹镊掖,還是感覺不夠深入乃戈,這個(gè)在解決異步問題上是一個(gè)很好的解決方案,所以詳細(xì)看一下堰乔,順便按照自己的思路實(shí)現(xiàn)一個(gè)簡(jiǎn)單的Promise偏化。