8000 feat: support stop event emit by mortalYoung · Pull Request #807 · DTStack/molecule · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat: support stop event emit #807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions src/common/event/__tests__/eventEmitter.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { EventEmitter } from '../index';
import type { ListenerEventContext } from '../index';

describe('Test the EventEmitter class', () => {
const event = new EventEmitter();
Expand Down Expand Up @@ -60,4 +61,41 @@ describe('Test the EventEmitter class', () => {
evt.unsubscribe(eventName);
expect(evt.count(eventName)).toBe(0);
});

test('Should stop delivering event', () => {
const evt = new EventEmitter();
const eventName = 'event1';

const mockFn = jest.fn();
evt.subscribe(eventName, mockFn);
evt.subscribe(eventName, function (this: ListenerEventContext) {
this.stopDelivery();
mockFn();
});
evt.subscribe(eventName, mockFn);

evt.emit(eventName);
expect(mockFn).toBeCalledTimes(2);
});

test('Should stop async function in event chain', () => {
const evt = new EventEmitter();
const eventName = 'event1';

const mockFn = jest.fn();
evt.subscribe(eventName, mockFn);
evt.subscribe(eventName, async function (this: ListenerEventContext) {
await new Promise<void>((resolve) => {
setTimeout(() => {
this.stopDelivery();
resolve();
}, 0);
});
mockFn();
});
evt.subscribe(eventName, mockFn);

evt.emit(eventName);
expect(mockFn).toBeCalledTimes(2);
});
});
20 changes: 17 additions & 3 deletions src/common/event/eventEmitter.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
export interface ListenerEventContext {
stopDelivery: () => void;
}

export class EventEmitter {
private _events = new Map<string, Function[]>();

Expand All @@ -9,9 +13,19 @@ export class EventEmitter {
public emit(name: string, ...args) {
const events = this._events.get(name);
if (events && events.length > 0) {
events.forEach((callEvent) => {
callEvent(...args);
});
let continued = true;
// call in descending order
for (let index = events.length - 1; index >= 0; index--) {
if (continued) {
const evt = events[index];
evt.call(
{
stopDelivery: () => (continued = false),
},
...args
);
}
}
}
}

Expand Down
59 changes: 59 additions & 0 deletions website/docs/advanced/event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
---
title: Event
sidebar_label: Event
---

An event actually is a callback function. The event-emitter calls the function when the corresponding event be emitted. For example, there is a button be called at editor, and it will emit an event called `molecule.editor.onClick`. And event-emitter will find the corresponding event according to the `molecule.editor.onClick` and then call it.

If there are multiples functions related to an event, these functions will be called in chain. And this is called **event chains**.

## Example

At most situations, we don't have to care about the event chains. Every functions whatever built-in or user-defined are all have to be called.

But if you want to stop the chain in some situations, you definitely have this situation, you could call `this.stopDelivery` to stop the delivery of event chains.

:::tips
The `this` context of the function is filled-in by Molecule.
:::

For example, if we want to stop delivery after calculating, and we can do like this:

```ts Stop delivery in synchronous
molecule.editor.onClick(() => {
// if result is true, and this function won't be called
console.log('onClick');
});

molecule.editor.onClick(function (this: ListenerEventContext) {
// do something
const result = calculated();
if (result) {
this.stopDelivery();
}
});
```

```ts Stop delivery in asynchronous
molecule.editor.onClick(() => {
// if result is true, and this function won't be called
console.log('onClick');
});

molecule.editor.onClick(async function (this: ListenerEventContext) {
await new Promise<void>((resolve) => {
setTimeout(() => {
this.stopDelivery();
resolve();
}, 0);
});
});
```

## Order

Functions in event chains are First In, Last Called(FILC). So the built-in functions are involved first, and called last.

Oppositely the user-defined functions are involved last, but called first.

So user-defined functions are ability to stop the delivery to the built-in functions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
title: 订阅事件
sidebar_label: 订阅事件
---

一个订阅事件本质上就是一个回调函数。事件触发器将会在对应事件被触发的时候执行对应的订阅事件。例如:在 editor 上有一个按钮,点击按钮将会触发 `molecule.editor.onClick` 该事件。那么事件触发器将会根据 `molecule.editor.onClick` 找到对应的事件,并执行它。

如果某个事件有被多处订阅,那么这个事件将会有很多回调函数。这些函数如果被触发将会被链式调用。我们称之为事件链。

## 示例

在大部分情况下,我们并不需要关心事件链的存在。每一个回调函数都有其存在的理由,不论是内置还是用户定义的函数。

然而如果在某些业务场景下,我们需要阻止事件链的传递(你必然会遇到这样子的场景),那么我们只需要调用 `this.stopDelivery` 来阻止事件链的传递即可。

:::tips
回调函数的(`this`)上下文会被 Molecule 修改。
:::

例如,如果我们想在计算获得某种结果后,阻止事件的传递,那么我们可以这样操作:

```ts 同步阻止事件传递
molecule.editor.onClick(() => {
// if result is true, and this function won't be called
console.log('onClick');
});

molecule.editor.onClick(function (this: ListenerEventContext) {
// do something
const result = calculated();
if (result) {
this.stopDelivery();
}
});
```

```ts 异步组织事件传递
molecule.editor.onClick(() => {
// if result is true, and this function won't be called
console.log('onClick');
});

molecule.editor.onClick(async function (this: ListenerEventContext) {
await new Promise<void>((resolve) => {
setTimeout(() => {
this.stopDelivery();
resolve();
}, 0);
});
});
```

## 顺序

事件链中的函数遵循先进后执行(FILC)。所以内置回调函数是先进后执行

相对的,用户定义的回调函数是后进先执行。所以用户定义的回调函数可以阻止事件传递到内置回调函数。
0