视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
JS事件先发布后订阅的方法
2020-11-27 19:59:31 责编:小采
文档
 这次给大家带来JS事件先发布后订阅的方法,实现JS事件先发布后订阅的注意事项有哪些,下面就是实战案例,一起来看一下。

之前写过一个的事件管理器,就是普通的先订阅后发布模式。但实际场景中我们需要做到后订阅的也能收到发布的消息。比如我们关注微信公众号,还是能看到历史消息的。类似于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。本文只是阐述了一个事件和方法的流程相关场景,也许对你有启发。

相信看了本文案例你已经掌握了方法,更多精彩请关注Gxl网其它相关文章!

推荐阅读:

vue的自定义动态组件使用详解

protobuf.js 与 Long.js的使用详解

下载本文
显示全文
专题