宣布定閱作為一種罕見的設想形式,在前端模塊化範疇能夠用來處理模塊輪迴依靠題目。
看一個簡樸的示例
// 音訊中間件v1
var msghub = (function() {
var listener = [];
return {
on: function(type, cb, option) {
listener[type] = listener[type] || [];
option = option || {};
listener[type].push({
cb: cb,
priority: option.priority || 0
});
},
fire: function(type, dataObj) {
if (listener[type]) {
listener[type].sort((a, b) => a.priority - b.priority).forEach((item) => {
item.cb.call(null, dataObj);
});
}
}
}
})();
以及音訊中間件的運用模塊
// a.js
msghub.on('data', function(data) {
console.log(data.val + 1); // 3
})
// b.js
msghub.on('data', function(data) {
console.log(data.val + 2); // 4
})
// c.js
msghub.fire('data', {
val: 2
});
當c模塊觸發data事宜的時刻,a和b模塊的監聽函數都邑被實行並輸出響應的效果。
定閱函數管道化
上面的例子基礎能夠滿足需求了,然則有時刻我們願望多個定閱函數之間能夠通報實行效果,相似linux管道a.pipe(b).pipe(c)…這類,上一個函數的輸出是下一個函數的輸入。針對這類管道化需求我們對msghub的回調遍歷從forEach改成reduce體式格局,以下代碼所示
// 音訊中間件v2 支撐實行效果通報
var msghub = (function() {
var listener = [];
option = option || {};
return {
on: function(type, cb, option) {
listener[type] = listener[type] || [];
listener[type].push({
cb: cb,
priority: option.priority || 0
});
},
fire: function(type, dataObj) {
if (listener[type]) {
listener[type].sort((a, b) => b.priority - a.priority).reduce((pre, cur) => {
let result = cur.cb.call(null, pre) || pre; // 假如一個定閱函數沒有返回值則通報上上個定閱函數的實行效果,假如須要完整的管道化的話就把|| pre去掉即可
return result;
}, dataObj);
}
}
}
})();
測試一下上面的msghub
// a.js
msghub.on('data', function(data) {
console.log('module a get num:' + data.val); // 3
return {
val: ++data.val
};
})
// b.js
msghub.on('data', function(data) {
console.log('module b get num:' + data.val)
return {
val: data.val + 3
}
})
// d.js
msghub.on('data', function(data) {
console.log('module d get num:' + data.val);
})
// e.js
msghub.on('data', function(data) {
console.log('module e get num:' + data.val);
})
// c.js
msghub.fire('data', {
val: 2
});
運用改進后的msghub的話
// a.js
msghub.on('data', function(data) {
console.log('module a get num:' + data.val); // 3
return {
val: ++data.val
};
})
// b.js
msghub.on('data', function(data) {
console.log('module b get num:' + data.val)
return {
val: data.val + 3
}
})
// d.js
msghub.on('data', function(data) {
console.log('module d get num:' + data.val);
})
// e.js
msghub.on('data', function(data) {
console.log('module e get num:' + data.val);
})
// c.js
msghub.fire('data', {
val: 2
});
終究打印輸出以下信息:
module a get num:2
module b get num:3
module d get num:6
module e get num:6
定閱函數支撐異步
上面的例子中有一個題目就是定閱函數必需是同步代碼,假如a.js包括下述異步代碼的話就會出題目
// a.js
msghub.on('data', function(data) {
console.log('module a get num:' + data.val); // 3
return new Promise(function(resolve, reject) {
setTimeout(() => {
resolve({
val: ++data.val
})
}, 1000);
});
})
針對能夠同步能夠異步的狀況我們須要進一步改進msghub來支撐,該請asyn和await進場了
// 音訊中間件v3 支撐異步管道化
var msghub = (function() {
var listener = [];
return {
on: function(type, cb, option) {
listener[type] = listener[type] || [];
option = option || {};
listener[type].push({
cb: cb,
priority: option.priority || 0
});
},
fire: function(type, dataObj) {
if (listener[type]) {
let listenerArr = listener[type].sort((a, b) => b.priority - a.priority);
(async function iter() {
let val = dataObj;
for (const item of listenerArr) {
val = await item.cb.call(null, val);
}
})();
}
}
}
})();
注重: 上述代碼能夠在node環境做測試,假如須要在瀏覽器中運轉的話,須要對for of和async await舉行babel編譯