之前写过一个的事件管理器,就是普通的先订阅后发布模式。但实际场景中我们需要做到后订阅的也能收到发布的消息。比如我们关注微信公众号,还是能看到历史消息的。类似于qq离线消息,我先发给你,你登录了就能收到了。就是确保订阅该事件的方法都能被执行

 var eventManger = {
cached: {},
handlers: {},
//类型,绑定事件
addHandler: function (type, handler) {
if (typeof handler !== "function") return; if (typeof this.handlers[type] == "undefined") {
this.handlers[type] = [];
}
this.handlers[type].push(handler); if (this.cached[type] instanceof Array) {
//说明有缓存的 可以执行
handler.apply(null, this.cached[type]);
}
},
removeHandler: function (type, handler) {
var events = this.handlers[type];
for (var i = 0, len = events.length; i < len; i++) {
if (events[i] == handler) {
events.splice(i, 1);
break;
}
}
},
trigger: function (type) {
//如果有订阅的事件,这个时候就触发了
if (this.handlers[type] instanceof Array) {
var handlers = this.handlers[type];
var args = Array.prototype.slice.call(arguments, 1);
for (var i = 0, len = handlers.length; i < len; i++) {
handlers[i].apply(null, args);
}
}
//默认缓存
this.cached[type] = Array.prototype.slice.call(arguments, 1);
}
};

其实就是增加了几行代码。缓存下最后一次触发的时的参数。 然后在addhandle的时候进行判断,如果订阅的时候已经有缓存的参数了,说明该方法可以执行了。

eventManger.addHandler("test", function (res) {
console.log("先订阅,后发布1", res);
}) eventManger.trigger("test", 2); eventManger.addHandler("test", function (res) {
console.log("先发布,后订阅2", res);
}) eventManger.addHandler("test", function (res) {
console.log("先发布,后订阅3", res);
})

我实际的场景是这样的A事件触发之后,才能执行B方法。但B方法需要在C方法完成之后。也就是B依赖于A和C的完成。且A几乎每次都会很快触发,当然可以设两个个开关变量和一个代理函数,等两个事件都完成之后再do B。代码如下:

var aReady = false;
var cReady = false;
eventManger.addHandler("A", function () {
aReady = true;
console.log("do A");
proxyC();
}); eventManger.trigger("A", 2); function doB() {
console.log("do B");
//实际B中的方法需要在A事件成功之后才能执行
} function doC() {
console.log("do C");
cReady = true;
proxyC();
} function proxyC() {
aReady && cReady && doB();
}
doC();

这样功能是实现了,但是可读性差了,而且事件订阅必须要对位置,如果在trigger之前,doB就永远执行不了,而且代码上多了两个变量和一个方法,最傻的是用一个变量加setTimeout去判断状态,这就可能陷入死循环。

var aReady = false;
eventManger.addHandler("A", function () {
aReady = true;
console.log("do A");
}); function doB() {
console.log("do B");
//实际B中的方法需要在A事件成功之后才能执行
} function doC() {
console.log("do C");
if (!aReady) {
console.log("wating...");
setTimeout(doC, 50);
return;
}
doB();
} doC(); eventManger.trigger("A", 2);//模拟A事件触发迟

这种办法最不可取吧。因为外部事件可能挂掉,这儿就走不出去了。等于是挖了个坑。但如果事件支持先发布,后订阅,问题就简单了:

eventManger.trigger("A", 2);

function doB() {
console.log("do B");
//实际B中的方法需要在A事件成功之后才能执行
} function doC() {
console.log("do c");
eventManger.addHandler("A", function () {
console.log("do a");
doB();
});
}
doC();

这样就清晰了很多。事件订阅也不必那么在意调用的位置了。以上只是记住最近的一次的调用参数,可以用于后订阅的事件触发。这适合一次性事件(一个周期只会触发一次的事件)。如果是像推送消息的事件,会不断的触发,如果想要确保也能获得全部的历史记录,就需要记住所有的参数。这是一种情况;实际可能还会有更多的流程依赖,当然对于流程控制有很多办法,也有很多库支持。比如promise和async。本文只是阐述了一个事件和方法的流程相关场景,也许对你有启发。

【JavaScript】让事件支持先发布后订阅的更多相关文章

  1. vue_组件间通信:自定义事件、消息发布与订阅、槽

    自定义事件 只能用于 子组件 向 父组件 发送数据 可以取代函数类型的 props 在父组件: 给子组件@add-todo-event="addTodo" 在子组件: 相关方法中, ...

  2. javascript 自定义事件 发布-订阅 模式 Event

    * javascript自定义事件 var myEvent = document.createEvent("Event"); myEvent.initEvent("myE ...

  3. javascript设计模式学习之八_发布订阅(观察者)模式

    一.发布订阅模式定义 jQuery中的callbacks,defered,promise本质上就是发布订阅模式的实现.ES6的promise内部实现未开源,不了解具体机制 发布订阅模式又叫做观察者模式 ...

  4. 理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码

    最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线 ...

  5. Dapr实现.Net Grpc服务之间的发布和订阅,并采用WebApi类似的事件订阅方式

    大家好,我是失业在家,正在找工作的博主Jerry,找工作之余,总结和整理以前的项目经验,动手写了个洋葱架构(整洁架构)示例解决方案 OnionArch.其目的是为了更好的实现基于DDD(领域驱动分析) ...

  6. C#事件支持发布者/订阅者模式(观察者模式)

    C#事件支持发布者/订阅者模式,发布者将事件通知给订阅者,而订阅者在事件发生时调用已经注册好的事件处理函数.        public delegate void delUpdate();  //委 ...

  7. Javascript事件模型系列(四)我所理解的javascript自定义事件

    被我拖延了将近一个月的javascript事件模型系列终于迎来了第四篇,也是我计划中的最后一篇,说来太惭愧了,本来计划一到两个星期写完的,谁知中间遇到了很多事情,公司的个人的,搞的自己心烦意乱浮躁了一 ...

  8. 最详细的JavaScript和事件解读

    与浏览器进行交互的时候浏览器就会触发各种事件.比如当我们打开某一个网页的时候,浏览器加载完成了这个网页,就会触发一个 load 事件:当我们点击页面中的某一个“地方”,浏览器就会在那个“地方”触发一个 ...

  9. [转] JavaScript 和事件

    与浏览器进行交互的时候浏览器就会触发各种事件.比如当我们打开某一个网页的时候,浏览器加载完成了这个网页,就会触发一个 load 事件:当我们点击页面中的某一个“地方”,浏览器就会在那个“地方”触发一个 ...

随机推荐

  1. (19)IO流之字符流FileReader和FileWriter,缓冲字符流---缓冲输入字符流BufferedReader和缓冲输出字符流BufferedWriter

    字符流,读取的文件是字符的时候,有两个基类一个是Reader,一个是Writer这有点拟人的感觉,人直接看懂的是文字 字符流 字节流:读取的是文件中的二进制字节流并不会帮你转换成看的懂得字符 字符流: ...

  2. 意外发现的大批量导入数据SqlBulkCopy类

    因为要做一个号码归属地查询小功能,因为要导入外部(文本文件)的电话归属地数据,使用的是SqlDataAdapter类,数据不多,只四万有多条,表也只有一个,phoneBook表,使用的是DataTab ...

  3. 多线程图像处理中对选入DC的位图保护

    我在应用多线程加速图像处理(具体参见图像处理的多线程计算)的过程中,曾遇到过一个线程同步的问题.多线程对图像不同子块进行处理,再合成.结果发现最终不是全部子块都处理成功,有的子块好像没有被处理.而且发 ...

  4. vue-router2.0简单路由嵌套

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  5. Where T:Class,new()的使用

    当我们使用泛型的时候,有时候就会提示我们T必须是引用类型而无法进行下去,其实我们学泛型的时候也应该了解到这个T的使用场合,他可以是值类型也可以是引用类型,但是我们某些场合就只能使用引用类型比如EF中的 ...

  6. 浅谈HTTP中Get与Post的区别[转载]

    Http定义了与服务器交互的不同方法,最基本的方法有4种,分别是GET,POST,PUT,DELETE.URL全称是资源描述符,我们可以这样认为:一个URL地址,它用于描述一个网络上的资源,而HTTP ...

  7. ubuntu12.04 desktop默认无ssh支持

    sudo apt-get install ssh 安装下即可.

  8. java 基础知识一 初识java

    java  基础知识一初识java 1.java语言的特点 (1)简洁有效(2)可移植性(3)面向对象(4)解释型(5)适合分布式计算 2.java的源文件扩展名为.java 编译后的扩展名为.cla ...

  9. Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  10. 浩哥解析MyBatis源码(八)——Type类型模块之TypeAliasRegistry(类型别名注册器)

    原创作品,可以转载,但是请标注出处地址:http://www.cnblogs.com/V1haoge/p/6705769.html 1.回顾 前面几篇讲了数据源模块,这和之前的事务模块都是enviro ...