记EventTarget和CallbacksInvoker回调顺序错乱的问题

测试代码:
testInvoker() {
const invoker = new EventTarget()
const fn1 = invoker.on(‘event’, () => {
console.log(’-------- handler1’);
invoker.off(‘event’, fn1);
});
const fn2 = invoker.on(‘event’, () => {
console.log(’-------- handler2’);
});
const fn3 = invoker.on(‘event’, () => {
console.log(’-------- handler3’);
});
const fn4 = invoker.on(‘event’, () => {
console.log(’-------- handler4’);
});
console.log("///////////////////////");
invoker.emit(‘event’);
console.log("///////////////////////");
invoker.emit(‘event’);
}

控制台输出:
///////////////////////
-------- handler1
-------- handler2
-------- handler3
-------- handler4
///////////////////////
-------- handler4
-------- handler2
-------- handler3

同一事件触发的处理逻辑应该是相互独立,和触发先后无关的。

引擎用的 fastremove ,删除第一个事件的处理函数时,会把最后一个该事件的处理函数移动到该位置。核心目的是提高删除性能。

你写逻辑,如果一个事件有前后处理关系的依赖,你应该写在同一个处理函数里面,这样处理逻辑不会分散(体现了耦合逻辑要集中的原则)。要不然理解你的处理逻辑就非常困难:
A 处要先注册先触发,B 处依赖了 A 处的处理才处理自己的逻辑。这简直是要命的设计。

事件系统有时候就是用来解耦的,有些情况下就是会分开注册的呀

已经解释了,不理解就算了。不强求。

Implement CallbacksInvoker using a linked list to avoid disorderly calling order caused by array.fastRemoveAt. by JoliChen · Pull Request #17705 · cocos/cocos-engine · GitHub 我提交了一个PR,测试看看。

说几个点:
第一点:数组的索引效率是高于链表。游戏是追求极致性能体验的。所以要性能都是第一考虑。
第二点:乱序到底是不是引擎在实现上的问题。我的观点是:不是设计问题。
第三点:用户依赖同一事件的多个注册函数的派发顺序的需求没办法依赖引擎层实现机制,问题出在谁身上?(这里我坚定的告诉你,是你设计上出了问题了。原因我前面解释过了)
游戏处理逻辑有两块 A 和 B,这里 B 依赖 A 先执行。你分两个地方写,逻辑就分散了。解耦不是这么解的。

开发中一个很大的原则,就是处理逻辑要尽可能集中。你 A 和 B 看似解耦,只是你代码实现上所谓派发以后,你写得更轻松。但是别人维护你代码,绝对是非常坑的。

确实不应该依赖事件的回调顺序,EventTarget 从设计上就没有保证回调顺序和注册顺序是一致的,

如果程序产生了这样的时序依赖,后续会很难维护

1赞

对比了两种执行时间,差不多的

有时候确实需要保证调用顺序,就像promise一样

em,不应该依赖事件派发顺序,
大喊一声,让每个听到的人按顺序听到,这就很奇怪。
默认是同时收到消息的,也就是不分先后

如果是分先后,那么应该有明确的代码先后,比如要解耦可以用类似生命周期
或者事件钩子,before,after

不需要解耦,就写在一起。

1赞

如果想依赖顺序,一般注册的时候都会有个触发的比重 比如 0-255 触发事件后 优先触发优先级高的事件或者消息,目前看 Cocos事件机制并没有想这么设计



image
image
链表的执行效率看起还好一些
@huaxing_zheng

你把链表实现的发来,晚点我测一下

这里核心问题是:你对于功能应该如何编写,存在设计上的错误,这种错误不是逻辑错误,而在于如何对人更友好的理解上。

Implement CallbacksInvoker using a linked list to avoid disorderly calling order caused by array.fastRemoveAt. by JoliChen · Pull Request #17705 · cocos/cocos-engine · GitHub 上面发了的

1赞

这样可以保证顺序了

image

没有用你的实现来测试。
我这边是用基于 Laya3.0 Delegate 模式的改造版本。

官方的实现其实本来就慢。

另外,你要用链表就用吧,自己喜欢就好了。

Laya3.0 Delegate 是按顺序的吗?C# 的delegate就是按添加顺序的,删除不会改变顺序。

Laya 的也不是按顺序的,也是 fastRemove 模式,不过它是用数组保存 func, ctx, param,还有一个 tag。
也就是注册一个事件的回调。占用数组长度为 4