资讯专栏INFORMATION COLUMN

发布订阅管道化

widuu / 828人阅读

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

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

看一个简单的示例
// 消息中间件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编译

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

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

相关文章

  • redis学习-第二节-部分高级

    摘要:事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。 redis 学习二 time : 20181018;学习一些Redis的高级应用--Redis 命令行 Redis 发布订阅 1. Redis 发布订阅(pub/sub)是一种消息通信模式:发送者发送消息,订阅者(sub)接受消息 2. Redis...

    dantezhao 评论0 收藏0
  • Kafka 文档(介绍)

    摘要:集群可以持久地保存所有已发布的记录无论它们是否被消费并使用可配置的保留期。有关这些保证的更多细节将在文档的设计部分中给出。与传统的消息传递系统相比,也有更强的顺序保证。这是通过将主题中的分区分配给消费者组中的消费者来实 介绍 Apache Kafka®是一个分布式流平台,这到底是什么意思? 流平台有三个关键功能: 发布和订阅记录流,类似于消息队列或企业消息传递系统 以容错持久的方式存...

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

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

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

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

    用户84 评论0 收藏0
  • Kafka 文档(用例)

    摘要:在我们的经验中,消息传递使用的吞吐量通常相对较低,但可能需要较低的端到端延迟,并且通常依赖于提供的强大的持久性保证。指标通常用于操作监控数据,这包括从分布式应用程序收集统计数据,以生产操作数据的集中提要。 用例 这是Apache Kafka®的一些流行用例的描述,有关这些领域的一些概况,请参阅这个博客。 消息传递 Kafka可以很好地替代更传统的消息代理,消息代理的使用有多种原因(从数...

    hearaway 评论0 收藏0

发表评论

0条评论

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