资讯专栏INFORMATION COLUMN

玩玩JS设计模式之:发布/订阅

zhaochunqi / 2941人阅读

摘要:前言的模块功能强大,除了常规的监听触发,还支持事件顺序,本文只是写着玩玩,真正要用的话,还是选择成熟稳定的东西较好内容概览以下订阅监听发布触发一般来说,先订阅事件,再发布事件就像打电话一样,电话没拨通订阅,你就开始说话要干嘛干嘛发布,这时候

前言

Node.js的 events 模块功能强大,除了常规的监听、触发,还支持事件顺序(prependListener),本文只是写着玩玩,真正要用的话,还是选择成熟稳定的东西较好!

内容概览: 以下订阅=监听、发布=触发;一般来说,先订阅事件再发布事件;就像打电话一样,电话没拨通(订阅),你就开始说话要干嘛干嘛(发布),这时候订阅是无效的!!!因为触发在前、监听在后,触发的时候没有监听,监听的时候已经结束,二者不在一个频道!!! 沟通就是无效的。。。

四个功能:

订阅on

订阅once(一次性

发布emit

注销off

错误监听error

构造函数
// 发布订阅,回调函数版本
function EvtEmit() {
  // 事件参数队列
  this.evtList = [];
}
原型
EvtEmit.prototype = {
  constructor: EvtEmit,
  // 订阅事件(监听)
  on(emitName, handler) {
    // console.debug(`EvtEmit -- on: ${emitName}`);
    if (!emitName) return;
    if (!this.evtList.some(evt => evt.emitName === emitName)) {
      this.evtList.push({
        emitName, // 事件名称
        handler,
        once: emitName === "error" ");true : false
      });
    }
  },
  // 订阅事件(一次性
  once(emitName, handler) {
    if (!emitName) return;
    if (!this.evtList.some(evt => evt.emitName === emitName)) {
      this.evtList.push({
        emitName, // 事件名称
        handler,
        once: true
      });
    }
  },
  // 发布事件(触发)
  emit(emitName, ...param) {
    // console.debug(`EvtEmit -- emit: ${emitName}`);
    if (!emitName) return;
    let evtThis = this.evtList.find(evt => evt.emitName === emitName);
    if (!evtThis) {
      if (emitName !== "error") {
        console.warn(`请先使用监听on("${emitName}", callback),再emit("${emitName}")!`);
      }
      return;
    }
    // 一次性订阅
    if (evtThis.once) this.off(emitName);

    // 监听[emitName]回调的错误
    try {
      evtThis.handler(...param);
    }
    catch (err) {
      // 不使用 on("error", callback)监听时,打印错误
      if (!this.evtList.some(evt => evt.emitName === "error")) {
        console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
      }

      // 错误触发,可以使用 on("error", callback)监听
      this.emit("error", {
        emitName,
        err
      });
    }
  },
  // 注销事件订阅
  off(emitName, callback = null) {
    let arr = this.evtList.filter(evt => evt.emitName !== emitName);
    this.evtList = arr;
    arr = null;
    if (callback) {
      callback.call(this, emitName);
    }
  }
};

使用

同正常的发布订阅一样,先订阅(on)再发布(emit);

const evt = new EvtEmit();

// 监听"run"事件
// 执行1次on监听,10次回调函数
evt.on("run", res => {
  console.log("res: ", res);
  // 注销监听,以下 emit 之后将不再触发 on;注释之后将无限调用
  if (--res < 1) {
    evt.off("run", emitName => {
      console.log(`on("${emitName}")已注销!`);
    });
    return;
  }

  evt.emit("run", res);
});

evt.emit("run", 10);

输出

错误监听

try/catch执行监听的回调函数,捕获错误然后触发emit("error", err),通过on("error", callback)监听错误;

为什么需要 try/catch");
不使用try/catch捕获错误的话,一旦发生错误,进程就挂了,这时,后续不需要依赖你这次操作结果的 程序就会跑不下去了!!!(如下 打印 "after go",如果没有try/catch,那么他就不会被打印);
这在服务端用的比较多,想象一下,一个接口因为某次调用的参数不合法或者其他因素,导致程序中断而影响到后续使用,可能产生‘事故’!

try/catch 包裹回调函数的执行
// 发布事件(触发)
  emit(emitName, ...param) {
    // console.debug(`EvtEmit -- emit: ${emitName}`);
    if (!emitName) return;
    let evtThis = this.evtList.find(evt => evt.emitName === emitName);
    if (!evtThis) {
      if (emitName !== "error") {
        console.warn(`请先使用监听on("${emitName}", callback),再emit("${emitName}")!`);
      }
      return;
    }
    // 一次性订阅
    if (evtThis.once) this.off(emitName);

    // 监听[emitName]回调的错误
    try {
      evtThis.handler(...param);
    }
    catch (err) {
      // 不使用 on("error", callback)监听时,打印错误
      if (!this.evtList.some(evt => evt.emitName === "error")) {
        console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
      }

      // 错误触发,可以使用 on("error", callback)监听
      this.emit("error", {
        emitName,
        err
      });
    }
  },
错误捕获:
evt.on("error", ({ emitName, err }) => {
  console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
})

evt.on("go", res => {
  err; // 错误会被 try/catch 捕获
  console.log("res: ", res);
});

evt.emit("go", "go");
console.log("after go"); // 没有 try/catch 的话,不会执行
全部代码 EvtEmit_callback.js:
// EvtEmit_callback.js
// 发布订阅,回调函数版本
function EvtEmit() {
  // 事件参数队列
  this.evtList = [];
}
EvtEmit.prototype = {
  constructor: EvtEmit,
  // 订阅事件(监听)
  on(emitName, handler) {
    // console.debug(`EvtEmit -- on: ${emitName}`);
    if (!emitName) return;
    if (!this.evtList.some(evt => evt.emitName === emitName)) {
      this.evtList.push({
        emitName, // 事件名称
        handler,
        once: emitName === "error" ");true : false
      });
    }
  },
  // 订阅事件(一次性
  once(emitName, handler) {
    if (!emitName) return;
    if (!this.evtList.some(evt => evt.emitName === emitName)) {
      this.evtList.push({
        emitName, // 事件名称
        handler,
        once: true
      });
    }
  },
  // 发布事件(触发)
  emit(emitName, ...param) {
    // console.debug(`EvtEmit -- emit: ${emitName}`);
    if (!emitName) return;
    let evtThis = this.evtList.find(evt => evt.emitName === emitName);
    if (!evtThis) {
      if (emitName !== "error") {
        console.warn(`请先使用监听on("${emitName}", callback),再emit("${emitName}")!`);
      }
      return;
    }
    // 一次性订阅
    if (evtThis.once) this.off(emitName);

    // 监听[emitName]回调的错误
    try {
      evtThis.handler(...param);
    }
    catch (err) {
      // 不使用 on("error", callback)监听时,打印错误
      if (!this.evtList.some(evt => evt.emitName === "error")) {
        console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
      }

      // 错误触发,可以使用 on("error", callback)监听
      this.emit("error", {
        emitName,
        err
      });
    }
  },
  // 注销事件订阅
  off(emitName, callback = null) {
    let arr = this.evtList.filter(evt => evt.emitName !== emitName);
    this.evtList = arr;
    arr = null;
    if (callback) {
      callback.call(this, emitName);
    }
  }
};

const evt = new EvtEmit();

// 执行1次on监听,10次回调函数
evt.on("run", res => {
  console.log("res: ", res);
  // 注销监听,以下 emit 之后将不再触发 on;注释之后将无限调用
  if (--res < 1) {
    evt.off("run", emitName => {
      console.log(`on("${emitName}")已注销!`);
    });
    return;
  }

  evt.emit("run", res);
});

evt.emit("run", 10);


// evt.on("error", ({ emitName, err }) => {
//   console.error(`on("${emitName}", callback) -> callback Error: ${err}`);
// })

// evt.on("go", res => {
//   err; // 错误会被 try/catch 捕获
//   console.log("res: ", res);
// });

// evt.emit("go", "go");
// console.log("after go"); // 没有 try/catch 的话,不会执行

参考

    js设计模式之发布/订阅模式模式

    events(事件触发器)

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

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

相关文章

  • JS设计模式Obeserver(观察者)模式、Publish/Subscribe(发布/订阅模式

    摘要:观察者模式定义设计模式中对的定义一个对象称为维持一系列依赖于它观察者的对象,将有关状态的任何变更自动通知给它们。如图模式比较观察者模式则多了一个类似于话题调度中心的流程,发布者和订阅者解耦。 Obeserver(观察者)模式 定义 《js设计模式》中对Observer的定义:一个对象(称为subject)维持一系列依赖于它(观察者)的对象,将有关状态的任何变更自动通知给它们。 《设计模...

    荆兆峰 评论0 收藏0
  • JavaScript设计模式发布-订阅模式(观察者模式)-Part1

    摘要:设计模式与开发实践读书笔记。发布订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系。附设计模式之发布订阅模式观察者模式数据结构和算法系列栈队列优先队列循环队列设计模式系列设计模式之策略模式 《JavaScript设计模式与开发实践》读书笔记。 发布-订阅模式又叫观察者模式,它定义了对象之间的一种一对多的依赖关系。当一个对象的状态发生改变时,所有依赖它的对象都将得到通知。 例...

    muzhuyu 评论0 收藏0
  • JavaScript设计模式发布-订阅模式(观察者模式)-Part2

    摘要:设计模式与开发实践读书笔记。看此文章前,建议先看设计模式之发布订阅模式观察者模式在中,已经介绍了什么是发布订阅模式,同时,也实现了发布订阅模式。 《JavaScript设计模式与开发实践》读书笔记。 看此文章前,建议先看JavaScript设计模式之发布-订阅模式(观察者模式)-Part1 在Part1中,已经介绍了什么是发布-订阅模式,同时,也实现了发布-订阅模式。但是,就Part1...

    Charlie_Jade 评论0 收藏0

发表评论

0条评论

zhaochunqi

|高级讲师

TA的文章

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