這是一個很是有趣的模式,它容許你經過對某個輸入做出反應來響應,而不是主動地檢查是否提供了輸入。換句話說,使用此模式,能夠指定你正在等待的輸入類型,並被動地等待,直到提供該輸入才執行代碼。git
在這裏,觀察者是一個對象,它們知道想要接收的輸入類型和要響應的操做,這些對象的做用是「觀察」另外一個對象並等待它與它們通訊。github
另外一方面,可觀察對象將讓觀察者知道什麼時候有新的輸入可用,以便他們能夠對它作出反應(若是適用的話)。若是這聽起來很熟悉,那是由於Node.js中處理事件的任何東西都是這種模式。數據庫
下面的代碼是一段HTTP Server的實現express
const http = require('http');
const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.end('Your own server here');
})
server.on('error', err => {
console.log(「Error:: 「, err)
})
server.listen(3000, '127.0.0.1', () => {
console.log('Server up and running');
})
複製代碼
在上面的代碼中隱藏了觀察者模式。服務器對象將充當可觀察對象,而回調函數是實際的觀察者。你能夠看到,這種模式很是適合這種HTTP異步調用。這種模式的另外一個普遍使用的用例是觸發特定事件。這種模式能夠在任何容易異步觸發事件(例如錯誤或狀態更新)的模塊上找到。例如數據庫驅動程序,甚至套接字。io,它容許你在本身代碼以外觸發的特定事件上設置觀察者。服務器
下面咱們簡單的實現一個EventEmitter類來實現觀察者模式:app
class eventEmitter {
constructor() {
this.eventObj = {}
}
on(evName, fn) {
this.eventObj[evName] = this.eventObj[evName] ? this.eventObj[evName].concat(fn) : [fn]
}
emit(evName, ...params) {
for (let fn of this.eventObj[evName]) {
fn.apply(null, params)
}
}
}
複製代碼
const event = new eventEmitter()
event.on('error', err => {
console.log(err, 1)
})
event.on('error', err => {
console.log(err, 2)
})
event.emit('error')
複製代碼
上面的例子中,error事件做爲一個被觀察對象,回調函數是觀察者,當觸發on事件時,觀察者會被加入到一個隊列中,當error事件觸發時,回調函數(觀察者)會受到通知,而且依次被調用。異步
職責鏈模式是Node.js世界中不少人使用過的一種模式,他們甚至沒有意識到這一點。函數
使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。post
下面是一個很是基本的實現這個模式,你能夠看到在底部,咱們有四個可能的值須要處理,可是咱們不在意誰來處理它們,咱們只須要,至少,一個函數來使用它們,所以咱們只是寄給鏈,讓鏈中的每個函數決定他們是否應該使用它或忽略它。ui
function processRequest(r, chain) {
let lastResult = null
let i = 0
do {
lastResult = chain[i](r)
i++
} while(lastResult != null && i < chain.length)
if(lastResult != null) {
console.log("Error: request could not be fulfilled")
}
}
let chain = [
function (r) {
if(typeof r == 'number') {
console.log("It's a number: ", r)
return null
}
return r
},
function (r) {
if(typeof r == 'string') {
console.log("It's a string: ", r)
return null
}
return r
},
function (r) {
if(Array.isArray(r)) {
console.log("It's an array of length: ", r.length)
return null
}
return r
}
]
processRequest(1, chain)
processRequest([1,2,3], chain)
processRequest('[1,2,3]', chain)
processRequest({}, chain)
複製代碼
輸出的結果是:
It's a number: 1
It's an array of length: 3
It's a string: [1,2,3]
Error: request could not be fulfilled
複製代碼
在咱們常常使用的開發庫中,這種模式最明顯的例子是ExpressJS的中間件。使用該模式,其實是在設置一系列函數(中間件),這些函數計算請求對象並決定對其運行一個函數或者忽略它。能夠將該模式看做上面示例的異步版本,在這個版本中,不是檢查函數是否返回值,而是檢查將哪些值傳遞給它們調用的下一個回調。
var app = express();
app.use(function (req, res, next) {
console.log('Time:', Date.now())
next(); //call the next function on the chain
})
複製代碼
中間件是這種模式的一種特殊實現,由於能夠認爲全部中間件均可以完成請求,而不是鏈中的一個成員。然而,其背後的原理是同樣的。
下面咱們來本身簡單實現一箇中間件函數
class App {
constructor() {
this.middleware = []
this.index = 0
}
use(fn) {
this.middleware.push(fn)
}
exec() {
this.next()
}
next() {
if (this.index < this.middleware.length) {
const fn = this.middleware[this.index]
this.index++
fn.call(this, this.next.bind(this))
}
}
}
const app = new App()
app.use(function (next) {
console.log(1)
next()
})
app.use(function (next) {
console.log(2)
})
app.exec()
複製代碼
輸出結果
1,2
複製代碼
當使用use函數時,在中間件隊列中加入回調函數,在執行時,能夠調用next()來進入下一個中間件。
上一篇咱們理解了IIFE和工廠模式還有單例模式
關注做者github,第一時間得到更新。