资讯专栏INFORMATION COLUMN

发布订阅管道化

widuu / 975人阅读

摘要:发布订阅作为一种常见的设计模式,在前端模块化领域可以用来解决模块循环依赖问题。订阅函数管道化上面的例子基本可以满足需求了,但是有时候我们希望多个订阅函数之间可以传递执行结果,类似管道这种,上一个函数的输出是下一个函数的输入。

发布订阅作为一种常见的设计模式,在前端模块化领域可以用来解决模块循环依赖问题。

看一个简单的示例

</>复制代码

  1. // 消息中间件v1
  2. var msghub = (function() {
  3. var listener = [];
  4. return {
  5. on: function(type, cb, option) {
  6. listener[type] = listener[type] || [];
  7. option = option || {};
  8. listener[type].push({
  9. cb: cb,
  10. priority: option.priority || 0
  11. });
  12. },
  13. fire: function(type, dataObj) {
  14. if (listener[type]) {
  15. listener[type].sort((a, b) => a.priority - b.priority).forEach((item) => {
  16. item.cb.call(null, dataObj);
  17. });
  18. }
  19. }
  20. }
  21. })();

以及消息中间件的使用模块

</>复制代码

  1. // a.js
  2. msghub.on("data", function(data) {
  3. console.log(data.val + 1); // 3
  4. })
  5. // b.js
  6. msghub.on("data", function(data) {
  7. console.log(data.val + 2); // 4
  8. })
  9. // c.js
  10. msghub.fire("data", {
  11. val: 2
  12. });

当c模块触发data事件的时候,a和b模块的监听函数都会被执行并输出相应的结果。

订阅函数管道化

上面的例子基本可以满足需求了,但是有时候我们希望多个订阅函数之间可以传递执行结果,类似linux管道a.pipe(b).pipe(c)…这种,上一个函数的输出是下一个函数的输入。针对这种管道化需求我们对msghub的回调遍历从forEach改为reduce方式,如下代码所示

</>复制代码

  1. // 消息中间件v2 支持执行结果传递
  2. var msghub = (function() {
  3. var listener = [];
  4. option = option || {};
  5. return {
  6. on: function(type, cb, option) {
  7. listener[type] = listener[type] || [];
  8. listener[type].push({
  9. cb: cb,
  10. priority: option.priority || 0
  11. });
  12. },
  13. fire: function(type, dataObj) {
  14. if (listener[type]) {
  15. listener[type].sort((a, b) => b.priority - a.priority).reduce((pre, cur) => {
  16. let result = cur.cb.call(null, pre) || pre; // 如果一个订阅函数没有返回值则传递上上个订阅函数的执行结果,如果需要完全的管道化的话就把|| pre去掉即可
  17. return result;
  18. }, dataObj);
  19. }
  20. }
  21. }
  22. })();

测试一下上面的msghub

</>复制代码

  1. // a.js
  2. msghub.on("data", function(data) {
  3. console.log("module a get num:" + data.val); // 3
  4. return {
  5. val: ++data.val
  6. };
  7. })
  8. // b.js
  9. msghub.on("data", function(data) {
  10. console.log("module b get num:" + data.val)
  11. return {
  12. val: data.val + 3
  13. }
  14. })
  15. // d.js
  16. msghub.on("data", function(data) {
  17. console.log("module d get num:" + data.val);
  18. })
  19. // e.js
  20. msghub.on("data", function(data) {
  21. console.log("module e get num:" + data.val);
  22. })
  23. // c.js
  24. msghub.fire("data", {
  25. val: 2
  26. });

使用改良后的msghub的话

</>复制代码

  1. // a.js
  2. msghub.on("data", function(data) {
  3. console.log("module a get num:" + data.val); // 3
  4. return {
  5. val: ++data.val
  6. };
  7. })
  8. // b.js
  9. msghub.on("data", function(data) {
  10. console.log("module b get num:" + data.val)
  11. return {
  12. val: data.val + 3
  13. }
  14. })
  15. // d.js
  16. msghub.on("data", function(data) {
  17. console.log("module d get num:" + data.val);
  18. })
  19. // e.js
  20. msghub.on("data", function(data) {
  21. console.log("module e get num:" + data.val);
  22. })
  23. // c.js
  24. msghub.fire("data", {
  25. val: 2
  26. });

最终打印输出如下信息:

</>复制代码

  1. module a get num:2
  2. module b get num:3
  3. module d get num:6
  4. module e get num:6
订阅函数支持异步

上面的例子中有一个问题就是订阅函数必须是同步代码,如果a.js包含下述异步代码的话就会出问题

</>复制代码

  1. // a.js
  2. msghub.on("data", function(data) {
  3. console.log("module a get num:" + data.val); // 3
  4. return new Promise(function(resolve, reject) {
  5. setTimeout(() => {
  6. resolve({
  7. val: ++data.val
  8. })
  9. }, 1000);
  10. });
  11. })

针对可能同步可能异步的情况我们需要进一步改良msghub来支持,该请asyn和await出场了

</>复制代码

  1. // 消息中间件v3 支持异步管道化
  2. var msghub = (function() {
  3. var listener = [];
  4. return {
  5. on: function(type, cb, option) {
  6. listener[type] = listener[type] || [];
  7. option = option || {};
  8. listener[type].push({
  9. cb: cb,
  10. priority: option.priority || 0
  11. });
  12. },
  13. fire: function(type, dataObj) {
  14. if (listener[type]) {
  15. let listenerArr = listener[type].sort((a, b) => b.priority - a.priority);
  16. (async function iter() {
  17. let val = dataObj;
  18. for (const item of listenerArr) {
  19. val = await item.cb.call(null, val);
  20. }
  21. })();
  22. }
  23. }
  24. }
  25. })();

注意: 上述代码可以在node环境做测试,如果需要在浏览器中运行的话,需要对for of和async await进行babel编译

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/94986.html

相关文章

  • 学习这些设计模式,让你写出更优雅的代码

    摘要:写代码容易,写出优雅的代码难,写易于维护的容易扩展的结构清晰的代码应该是每位开发者努力的目标,而学习设计模式,合理的的使用能让我们离这个目标更进一步。 写代码容易,写出优雅的代码难,写易于维护的、容易扩展的、结构清晰的代码应该是每位开发者努力的目标,而学习设计模式,合理的的使用能让我们离这个目标更进一步。最近看了《Javascript设计模式与开发实践》这本书,一言以蔽之,真不错的一本...

    songjz 评论0 收藏0
  • 学习这些设计模式,让你写出更优雅的代码

    摘要:写代码容易,写出优雅的代码难,写易于维护的容易扩展的结构清晰的代码应该是每位开发者努力的目标,而学习设计模式,合理的的使用能让我们离这个目标更进一步。 写代码容易,写出优雅的代码难,写易于维护的、容易扩展的、结构清晰的代码应该是每位开发者努力的目标,而学习设计模式,合理的的使用能让我们离这个目标更进一步。最近看了《Javascript设计模式与开发实践》这本书,一言以蔽之,真不错的一本...

    用户84 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<