@6pm/emit 中文文档教程

发布于 8年前 浏览 62 项目主页 更新于 3年前

6pm/emit

版本 npm构建状态Coverage Status

具有惰性初始化、零内存和 cpu 的零依赖事件发射器 初始状态,并完全支持 Symbol 事件类型!

Installation

通过 npm 添加 @6pm/emit 到你的项目。

$ npm install @6pm/emit --save

Testing

首先获取 github 存储库的本地克隆。

$ git clone https://github.com/6pm-js/emit.git
$ cd emit

运行测试套件:

$ npm test

或使用 istanbul 生成覆盖率报告。

$ npm run cover

A note on ES2015

@6pm/emit 是使用 ES2015 特性编写的,因此直接兼容 仅适用于 Node 版本 >= 4.0.0 或最新的常青浏览器。

理论上,Babel 或者 Traceur 可用于 transpile 以与相当旧的 JavaScript 引擎兼容 - 如果 必需的。

@6pm/emit 模块也通过 ES2015 模块公开,尽管 NPM 包在 dist/emit.js 中包含一个 UMD 包装构建, 适合包含在常青浏览器中,或直接通过 require 在节点中。

当直接在网络浏览器中使用 UMD 包装构建时,Emit 类是 通过全局 sixpm.emit.Emit 公开。

Usage

与核心 NodeJS 大部分兼容 EventEmitter, except:

  • addition of explicit context extensions, as defined by eventemitter3
  • removal of the 'uncaught error event throws' behaviour - this is very counterintuitive - throw or emit, pick one, or explicitly do both.
  • removal of the expensive and pointless newListener and removeListener events.
  • Total ignorance of the max listeners setting and its very arbitrary warning.

要将类变成事件发射器,有两种主要方法:

  • by extension
  • by prototypical enhancement

By extension

扩展 Emit 类为子类提供事件发射器功能。

import { Emit } from '@6pm/emit';

class SomeNewEmitter extends Emit {}

let instance    = new SomeNewEmitter(),
    EVENT       = Symbol('my.event');


instance.on(EVENT, () => { /* Do some work. */ });

instance.emit(EVENT, 1, 2, 3);

By prototypical enhancement

静态 Emit.assign() 方法将事件发射器功能分配给 目标对象,可以是静态对象,也可以是类原型。

因为 6pm/emit 最初是无状态的,所以没有构造函数重载 必需 - assigning Just Works™。

import { Emit as EventEmitter } from '@6pm/emit';

class SomeNewEmitter {

    constructor() {
        // Note: no Emit based custom construction required.
    }
}

EventEmitter.assign(SomeNewEmitter.prototype);

let instance = new SomeNewEmitter(),

instance.on('some-event', () => { /* Do some work. */ });

instance.emit('some-event', result, false);

Context enhancement

以下方法支持将 context 作为可选的第三个传递 参数,作为避免 bind 函数到 a 的开销的快捷方式 context - 从 eventemitter3 中无耻地窃取的想法。

  • on
  • once
  • off
  • addListener
  • removeListener

因此,以下:

someEmitter.on('event', callback, context);

等同于但快于:

someEmitter.on('event', callback.bind(context));

Design

@6pm/emit 旨在解决 JavaScript 中非常常见的设计模式。

即,事件发射和消费明显不相交 - 图书馆 通常会发出许多事件,以便公开托管的完整生命周期 信息,但应用程序很少使用它们——而是挑选一个 必要的很少。

Zero initial state

该模块不假定任何给定事件发射器的初始事件状态,而是 仅在首次注册听众时增加发射器, 具有可以对隐藏的发射器状态做出安全假设的“快速路径”。

这意味着没有 cpu 开销或事件的空间要求 发射器,除非应用程序实际上监听它们。

权衡是增强的“快速路径”方法需要额外的 注册监听器后的空间 - 但对于有许多对象的应用程序 暴露事件发射器接口,但很少使用,这有很大的影响 关于资源使用和初始化性能。

Encapsulated internals

所有发射器状态都通过 ES2015 Symbol 隐藏,以防止冲突,并且 在设计中提供一定程度的封装 - 虽然显然不是完美的 防止信息泄露的方法 (getOwnPropertySymbols 允许揭开面纱)

  • this is generally safer than using named properties.

Symbol support

Symbol s 作为事件类型在整个过程中都得到支持和显式测试 模块 - 作为个人喜好的有效分离 / Symbol 作为事件的命名空间和可读性似乎是一个很好的方法。

事件生产:

import { Emit } from '@6pm/emit';

const START = Symbol('someclass.start.event');

export class SomeClass {

    start() {
        this.emit(START);
    }

}

SomeClass.START = START;

事件消费:

import { SomeClass } from 'someclass.js';

let instance = SomeClass();

// No potential for event name collision, elegant reference to source of event.
instance.on(SomeClass.START, () => { /* ... */ });

Performance

这个模块的主要目的是提供一个(大部分)与Node兼容的 API,为未订阅的发射器强制执行零初始状态,以确保强大的 Symbol 支持(由于替代库中的混合体验),并使用 干净的 ES2015 代码风格。

性能优化是次要问题,尽管通过折叠 从 eventemitter3 的出色工作中吸取的教训 团队,然后继续削减发现的任何多余部分,此实施具有 变得异常高效 - 微基准测试是一个相当糟糕的衡量标准 真实世界的性能 - 但如果需要,添加 @6pm/emit 是相当简单的 到 eventemitter3 的基准套件来评估原始开销 - 和 结果 (与适量的盐一起食用)显示 @6pm/emit 没有 特定的弱点,并在大约一半的情况下取得最高绩效 测试。

TODO:添加性能测试和资源监控工具以允许 对此在野外进行综合测试。

Contributing

欢迎所有评论、批评、PR 和问题!

License

MIT 许可下发布

6pm/emit

Version npmBuild StatusCoverage Status

A zero dependency event emitter with lazy initialisation, zero memory and cpu initial state, and full support for Symbol event types!

Installation

Add @6pm/emit to your project via npm.

$ npm install @6pm/emit --save

Testing

First grab a local clone of the github repo.

$ git clone https://github.com/6pm-js/emit.git
$ cd emit

The run the test suite:

$ npm test

Or use istanbul to produce a coverage report.

$ npm run cover

A note on ES2015

@6pm/emit is written using ES2015 features, and as such is directly compatible only with Node versions >= 4.0.0, or recent evergreen browsers.

In theory, Babel or Traceur could be used to transpile for compatibility with considerably older JavaScript engines - if required.

The @6pm/emit module is also exposed via a ES2015 module, though the NPM package includes a UMD wrapped build in dist/emit.js, suitable for inclusion in evergreen browsers, or directly accessing via require in Node.

When using the UMD wrapped build directly in a web browser, the Emit class is exposed via the global sixpm.emit.Emit.

Usage

Mostly compatible with the core NodeJS EventEmitter, except:

  • addition of explicit context extensions, as defined by eventemitter3
  • removal of the 'uncaught error event throws' behaviour - this is very counterintuitive - throw or emit, pick one, or explicitly do both.
  • removal of the expensive and pointless newListener and removeListener events.
  • Total ignorance of the max listeners setting and its very arbitrary warning.

To turn a class into an event emitter, there are two primary approaches:

  • by extension
  • by prototypical enhancement

By extension

Extending the Emit class provides event emitter capabilities to the sub class.

import { Emit } from '@6pm/emit';

class SomeNewEmitter extends Emit {}

let instance    = new SomeNewEmitter(),
    EVENT       = Symbol('my.event');


instance.on(EVENT, () => { /* Do some work. */ });

instance.emit(EVENT, 1, 2, 3);

By prototypical enhancement

The static Emit.assign() method assigns event emitter capabilities to the target object, which can be a static object, or a class prototype.

Because 6pm/emit is initially stateless, no constructor overloading is required - assigning Just Works™.

import { Emit as EventEmitter } from '@6pm/emit';

class SomeNewEmitter {

    constructor() {
        // Note: no Emit based custom construction required.
    }
}

EventEmitter.assign(SomeNewEmitter.prototype);

let instance = new SomeNewEmitter(),

instance.on('some-event', () => { /* Do some work. */ });

instance.emit('some-event', result, false);

Context enhancement

The following methods support a context being passed as an optional third argument, as a shortcut to avoid the overhead of binding a function to a context - an idea brazenly stolen from eventemitter3.

  • on
  • once
  • off
  • addListener
  • removeListener

So, the following:

someEmitter.on('event', callback, context);

is equivalent to, but considerably faster than:

someEmitter.on('event', callback.bind(context));

Design

@6pm/emit is designed to address a very common design pattern in JavaScript.

Namely, that event emission, and consumption are notably disjoint - libraries typically emit many events, in order to expose complete lifecycles of managed information, but applications rarely consume them all - instead cherry picking a necessary few as required.

Zero initial state

This module assumes no initial event state for any given event emitter, instead augmenting emitters only when listeners are registered for the first time, with a 'fast path' that can make safe assumptions about hidden emitter state.

This means that there is no cpu overhead, or space requirements for event emitters, unless an application actually listens to them.

The trade off is that the augmented 'Fast path' methods require additional space once listeners are registered - but for applications where many objects expose event emitter interfaces, but few are used, this has a significant impact on resource usage, and initialisation performance.

Encapsulated internals

All emitter state is hidden via ES2015 Symbols, to prevent clashes, and provide a degree of encapsulation in the design - though obviously not a perfect approach to preventing information leakage (getOwnPropertySymbols allows peering behind the veil)

  • this is generally safer than using named properties.

Symbol support

Symbols as event types are supported, and explicitly tested for, throughout the module - as a matter of personal preference the effective separation / namespacing and readability of Symbols as events seems like a great approach.

Event production:

import { Emit } from '@6pm/emit';

const START = Symbol('someclass.start.event');

export class SomeClass {

    start() {
        this.emit(START);
    }

}

SomeClass.START = START;

Event consumption:

import { SomeClass } from 'someclass.js';

let instance = SomeClass();

// No potential for event name collision, elegant reference to source of event.
instance.on(SomeClass.START, () => { /* ... */ });

Performance

The primary purpose of this module is to provide a (mostly) Node compatible API, to enforce zero initial state for unsubscribed emitters, to ensure strong Symbol support (due to mixed experiences in alternative libraries), and to use a clean ES2015 code style.

Performance optimisation is a secondary concern, though by folding in the lessons learned by the sterling work of the eventemitter3 team, and then continuing to cut any excesses found, this implementation has become exceptionally efficient - microbenchmarks are a fairly poor measure of real world performance - but if desired, it is fairly trivial to add @6pm/emit to eventemitter3's benchmark suite to asses raw overheads - and the results (to be consumed with a suitable serving of salt) show @6pm/emit as having no particular weaknesses, and achieving the highest performance in around half of the tests.

TODO: Add performance testing and resource monitoring tooling to allow comprehensive testing of this in the wild.

Contributing

All comments, criticisms, PRs, and Issues welcome!

License

Release under the MIT license

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文