【Node】node的Events模块(事件模块)的介绍和使用

文章目录

  • 简言
  • Events
    • Passing arguments and this to listeners 向监听器传递参数
    • Asynchronous vs. synchronous 异步和同步
    • Handling events only once 只一次处理事件
    • Error events 错误事件
    • Capture rejections of promises 捕捉拒绝承诺的情况
    • Class: EventEmitter 事件类
      • Event: 'newListener' 新监听事件
      • Event: 'removeListener' 移除监听事件
      • emitter.addListener(eventName, listener) 添加监听器
      • emitter.emit(eventName[, ...args]) 触发监听器
      • emitter.eventNames() 获取已注册监听器名称列表
      • emitter.getMaxListeners() 获取最大监听器值
      • emitter.listenerCount(eventName[, listener]) 获取指定监听器数量
      • emitter.listeners(eventName) 获取指定监听器列表
      • emitter.off(eventName, listener) 移除监听
      • emitter.on(eventName, listener) 注册监听器
      • emitter.once(eventName, listener) 注册一次性监听器
      • emitter.prependListener(eventName, listener) 前面添加监听器
      • emitter.prependOnceListener(eventName, listener) 前面添加一次性监听器
      • emitter.removeAllListeners([eventName]) 移除指定名称的所有监听器
      • emitter.removeListener(eventName, listener) 移除指定名称的监听器
      • emitter.setMaxListeners(n) 设置最大监听器值
      • emitter.rawListeners(eventName) 获取指定侦听器副本
      • emitter[Symbol.for('nodejs.rejection')](err, eventName[, ...args]) 拒绝承诺函数
    • events.defaultMaxListeners 默认最大监听器值
    • events.errorMonitor 监听错误监听器
    • events.getEventListeners(emitterOrTarget, eventName) 获取指定事件监听器数组副本
    • events.getMaxListeners(emitterOrTarget) 获取当前监听器最大数量
    • events.once(emitter, name[, options]) 创建监听emitter错误的promise
      • Awaiting multiple events emitted on process.nextTick() 等待 process.nextTick() 发出的多个事件
    • events.captureRejections 全局 captureRejections 选项
    • events.captureRejectionSymbol
    • events.listenerCount(emitter, eventName)
    • events.on(emitter, eventName[, options]) 用于迭代由发射器发射的 eventName 事件
    • events.setMaxListeners(n[, ...eventTargets]) 设置最大监听器数量
    • Class: events.EventEmitterAsyncResource extends EventEmitter
      • new events.EventEmitterAsyncResource([options]) 创建EventEmitterAsyncResource实例
      • eventemitterasyncresource.asyncId 分配给资源的唯一 asyncId。
      • eventemitterasyncresource.asyncResource 底层 \<AsyncResource>。
      • eventemitterasyncresource.emitDestroy() 销毁事件
      • eventemitterasyncresource.triggerAsyncId
    • EventTarget and Event API
    • Node.js EventTarget vs. DOM EventTarget
    • NodeEventTarget 比较 EventEmitter
    • Event listener 事件监听器
    • EventTarget error handling EventTarget 错误处理
    • Class: Event Event类
      • event.bubbles
      • event.cancelBubble
      • event.cancelable
      • event.composed
      • event.composedPath()
      • event.currentTarget
      • event.defaultPrevented
      • event.eventPhase
      • event.initEvent(type[, bubbles[, cancelable]])
      • event.isTrusted
      • event.preventDefault()
      • event.returnValue
      • event.srcElement
      • event.stopImmediatePropagation()
      • event.stopPropagation()
      • event.target
      • event.timeStamp
      • event.type
    • Class: EventTarget
      • eventTarget.addEventListener(type, listener[, options])
      • eventTarget.dispatchEvent(event)
      • eventTarget.removeEventListener(type, listener[, options])
    • Class: CustomEvent
      • event.detail
    • Class: NodeEventTarget
      • nodeEventTarget.addListener(type, listener) 添加
      • nodeEventTarget.emit(type, arg) 触发
      • nodeEventTarget.eventNames()
      • nodeEventTarget.listenerCount(type)
      • nodeEventTarget.setMaxListeners(n)
      • nodeEventTarget.getMaxListeners()
      • nodeEventTarget.off(type, listener[, options])
      • nodeEventTarget.on(type, listener)
      • nodeEventTarget.once(type, listener)
      • nodeEventTarget.removeAllListeners([type])
      • nodeEventTarget.removeListener(type, listener[, options])
  • 结语

简言

Node.js 的大部分核心 API 都是围绕一种惯用的异步事件驱动架构构建的,在这种架构中,某些类型的对象(称为 “发射器”)会发射命名事件,从而导致函数对象(称为 “监听器”)被调用。

当我们学会events模块后,就可以自定义事件监听和触发时机了。

Events

Node.js 的大部分核心 API 都是围绕一种惯用的异步事件驱动架构构建的,在这种架构中,某些类型的对象(称为 “发射器”)会发射命名事件,从而导致函数对象(称为 “监听器”)被调用。
例如:net.Server 对象会在每次有对等设备连接到它时发出一个事件;fs.ReadStream 会在文件打开时发出一个事件;数据流会在有数据可读取时发出一个事件。
所有发射事件的对象都是 EventEmitter 类的实例。这些对象会暴露一个 eventEmitter.on() 函数,该函数允许将一个或多个函数附加到对象发出的已命名事件上。通常情况下,事件名称是驼峰字符串,但也可以使用任何有效的 JavaScript 属性键。
当 EventEmitter 对象发出一个事件时,所有与该特定事件相关的函数都会被同步调用。被调用的监听器返回的任何值都将被忽略和丢弃。
下面的示例显示了一个带有单个监听器的简单 EventEmitter 实例。eventEmitter.on() 方法用于注册监听器,而 eventEmitter.emit() 方法用于触发事件。

import { EventEmitter } from 'node:events';class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();
myEmitter.on('event', () => {console.log('an event occurred!');
});
myEmitter.emit('event');

Passing arguments and this to listeners 向监听器传递参数

eventEmitter.emit() 方法允许将任意一组参数传递给监听器函数。请记住,在调用普通监听器函数时,标准 this 关键字会被有意设置为引用监听器所连接的 EventEmitter 实例。

import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', function(a, b) {console.log(a, b, this, this === myEmitter);// Prints://   a b MyEmitter {//     _events: [Object: null prototype] { event: [Function (anonymous)] },//     _eventsCount: 1,//     _maxListeners: undefined,//     [Symbol(shapeMode)]: false,//     [Symbol(kCapture)]: false//   } true
});
myEmitter.emit('event', 'a', 'b');

可以使用 ES6 箭头函数作为监听器,但这样做时,this 关键字将不再引用 EventEmitter 实例:

import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('event', (a, b) => {console.log(a, b, this);// Prints: a b undefined
});
myEmitter.emit('event', 'a', 'b');

Asynchronous vs. synchronous 异步和同步

**EventEmitter 会按照注册的顺序同步调用所有监听器。**这可确保事件的正确排序,并有助于避免竞赛条件和逻辑错误。在适当的时候,监听器函数可以使用 setImmediate() 或 process.nextTick() 方法切换到异步操作模式:

import { EventEmitter } from "node:events";class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on("event", (a, b) => {setImmediate(() => {console.log("this happens asynchronously");});console.log(a, b);
});
myEmitter.emit("event", "a", "b");

Handling events only once 只一次处理事件

如果使用 eventEmitter.on() 方法注册了监听器,那么每次指定的事件发生时,都会调用该监听器。
使用 eventEmitter.once() 方法可以注册一个监听器,该监听器最多只能被调用一次。一旦事件发生,监听器就会被取消注册,然后再被调用。

let m = 0;
myEmitter.once("event", () => {console.log(++m);
});
myEmitter.emit("event");
// Prints: 1
myEmitter.emit("event");
// Ignored 忽略

Error events 错误事件

当 EventEmitter 实例发生错误时,典型的操作是发出一个 "error"事件。这些在 Node.js 中被视为特殊情况。
如果一个 EventEmitter 没有为 "error"事件注册至少一个监听器,并且发出了 "错误 "事件,则会抛出错误、打印堆栈跟踪并退出 Node.js 进程。
为了防止 Node.js 进程崩溃,可以使用 domain 模块。(但请注意,node:domain 模块已被弃用)。
作为最佳实践,应始终为 "error"事件添加监听器。

import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();
myEmitter.on('error', (err) => {console.error('whoops! there was an error');
});
myEmitter.emit('error', new Error('whoops!'));
// Prints: whoops! there was an error

通过使用符号 events.errorMonitor 安装一个监听器,可以监听 "错误 "事件,而无需消耗所发出的错误。

import { EventEmitter, errorMonitor } from 'node:events';const myEmitter = new EventEmitter();
myEmitter.on(errorMonitor, (err) => {MyMonitoringTool.log(err);
});
myEmitter.emit('error', new Error('whoops!'));
//	触发错误事件监听,仍会抛出 Node.js 并导致其崩溃

Capture rejections of promises 捕捉拒绝承诺的情况

在事件处理程序中使用异步函数是有问题的,因为在抛出异常的情况下,它会导致未处理的拒绝:

import { EventEmitter } from 'node:events';
const ee = new EventEmitter();
ee.on('something', async (value) => {throw new Error('kaboom');
});

EventEmitter 构造函数中的 captureRejections 选项或全局设置会改变这种行为,在 Promise 上安装 .then(undefined, handler) 处理程序。

const ee = new EventEmitter({ captureRejections: true });
ee.on("something", async (value) => {throw new Error("kaboom");
});
ee.on("error", console.log);
ee[Symbol.for("nodejs.rejection")] = () => {console.log("捕捉到了");
};ee.emit("something");

设置 events.captureRejections = true 将更改 EventEmitter 所有新实例的默认值。

import { EventEmitter } from 'node:events';EventEmitter.captureRejections = true;
const ee1 = new EventEmitter();
ee1.on('something', async (value) => {throw new Error('kaboom');
});ee1.on('error', console.log);

由 captureRejections 行为生成的 "错误 "事件没有 catch 处理程序,以避免无限错误循环:建议不要使用 async 函数作为 "错误 "事件处理程序。

Class: EventEmitter 事件类

EventEmitter 类可以用于创建事件监听和事件触发实例,由 node:events 模块定义并公开:

import { EventEmitter } from 'node:events';

当添加新的侦听器时,所有 EventEmitters 都会发出 "newListener "事件;当移除现有侦听器时,所有 EventEmitters 都会发出 "removeListener "事件。
它支持以下选项:

  • captureRejections <boolean>启用自动捕捉承诺拒绝。默认值:false。

Event: ‘newListener’ 新监听事件

参数:

  • eventName —— 正在监听的事件名称。
  • listener —— 事件处理函数。

在监听器被添加到其内部的监听器数组之前,EventEmitter 实例会发出自己的 "newListener "事件。
为 "newListener "事件注册的监听器会收到事件名称和被添加监听器的引用。
事实上,在添加监听器之前触发事件有一个微妙但重要的副作用:在 "newListener "回调中以相同名称注册的任何其他监听器都会被插入到正在添加的监听器之前。

import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}const myEmitter = new MyEmitter();
// Only do this once so we don't loop forever
myEmitter.once('newListener', (event, listener) => {if (event === 'event') {// Insert a new listener in frontmyEmitter.on('event', () => {console.log('B');});}
});
myEmitter.on('event', () => {console.log('A');
});
myEmitter.emit('event');
// Prints:
//   B
//   A

Event: ‘removeListener’ 移除监听事件

参数:

  • eventName <string> | <symbol> 事件名称
  • listener <函数> 事件处理函数
    移除监听器后会发出 "removeListener "事件。

emitter.addListener(eventName, listener) 添加监听器

参数:

  • eventName <字符串> | <符号> 事件名称
  • listener <Function> 事件处理函数
    emitter.on(eventName, listener) 的别名。

emitter.emit(eventName[, …args]) 触发监听器

同步调用为名为 eventName 的事件注册的每个监听器,调用顺序与注册顺序一致,并将提供的参数传递给每个监听器。

如果事件有监听者,则返回 true,否则返回 false。

import { EventEmitter } from 'node:events';
const myEmitter = new EventEmitter();// First listener
myEmitter.on('event', function firstListener() {console.log('Helloooo! first listener');
});
// Second listener
myEmitter.on('event', function secondListener(arg1, arg2) {console.log(`event with parameters ${arg1}, ${arg2} in second listener`);
});
// Third listener
myEmitter.on('event', function thirdListener(...args) {const parameters = args.join(', ');console.log(`event with parameters ${parameters} in third listener`);
});console.log(myEmitter.listeners('event'));myEmitter.emit('event', 1, 2, 3, 4, 5);// Prints:
// [
//   [Function: firstListener],
//   [Function: secondListener],
//   [Function: thirdListener]
// ]
// Helloooo! first listener
// event with parameters 1, 2 in second listener
// event with parameters 1, 2, 3, 4, 5 in third listener

emitter.eventNames() 获取已注册监听器名称列表

返回一个数组,其中列出发射器已注册监听器的事件。数组中的值为字符串或符号。

import { EventEmitter } from 'node:events';const myEE = new EventEmitter();
myEE.on('foo', () => {});
myEE.on('bar', () => {});const sym = Symbol('symbol');
myEE.on(sym, () => {});console.log(myEE.eventNames());
// Prints: [ 'foo', 'bar', Symbol(symbol) ]

emitter.getMaxListeners() 获取最大监听器值

返回事件发射器的当前最大监听器值,该值由 emitter.setMaxListeners(n) 设置或默认为 events.defaultMaxListeners。

emitter.listenerCount(eventName[, listener]) 获取指定监听器数量

返回监听名为 eventName 的事件的监听者数量。如果提供了监听器,则将返回监听器在事件监听器列表中被找到的次数。

emitter.listeners(eventName) 获取指定监听器列表

返回名为 eventName 的事件的侦听器数组副本。

emitter.off(eventName, listener) 移除监听

emitter.removeListener() 的别名。

emitter.on(eventName, listener) 注册监听器

将监听器函数添加到名为 eventName 的事件的监听器数组末尾。不会检查是否已添加监听器。多次调用事件名和监听器的相同组合将导致监听器被多次添加和调用。

server.on('connection', (stream) => {console.log('someone connected!');
}); 

返回对 EventEmitter 的引用,以便进行链式调用。
默认情况下,事件监听器会按照添加的顺序被调用。可以使用 emitter.prependListener() 方法将事件监听器添加到监听器数组的开头。

import { EventEmitter } from 'node:events';
const myEE = new EventEmitter();
myEE.on('foo', () => console.log('a'));
myEE.prependListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
//   b
//   a

emitter.once(eventName, listener) 注册一次性监听器

为名为 eventName 的事件添加一次性监听器函数。下次事件名称被触发时,该监听器将被移除,然后再被调用。
返回对 EventEmitter 的引用,以便进行链式调用。
默认情况下,事件监听器会按照添加的顺序被调用。可以使用 emitter.prependOnceListener() 方法将事件监听器添加到监听器数组的开头。

import { EventEmitter } from 'node:events';
const myEE = new EventEmitter();
myEE.once('foo', () => console.log('a'));
myEE.prependOnceListener('foo', () => console.log('b'));
myEE.emit('foo');
// Prints:
//   b
//   a

emitter.prependListener(eventName, listener) 前面添加监听器

将监听器函数添加到名为 eventName 的事件的监听器数组开头。不会检查是否已添加监听器。多次调用事件名和监听器的相同组合将导致监听器被多次添加和调用。
返回对 EventEmitter 的引用,以便进行链式调用。

server.prependListener('connection', (stream) => {console.log('someone connected!');
}); 

emitter.prependOnceListener(eventName, listener) 前面添加一次性监听器

将名为 eventName 的事件的一次性监听函数添加到监听器数组的开头。下次事件名称被触发时,该监听器将被移除,然后再被调用。

server.prependOnceListener('connection', (stream) => {console.log('Ah, we have our first user!');
}); 

返回对 EventEmitter 的引用,以便进行链式调用。

emitter.removeAllListeners([eventName]) 移除指定名称的所有监听器

移除所有监听器或指定事件名称的监听器。
移除代码中其他地方添加的监听器是不好的做法,尤其是当 EventEmitter 实例是由其他组件或模块(如套接字或文件流)创建时。
返回对 EventEmitter 的引用,以便进行链式调用。

emitter.removeListener(eventName, listener) 移除指定名称的监听器

从名为 eventName 的事件的监听器数组中移除指定的监听器。

const callback = (stream) => {console.log('someone connected!');
};
server.on('connection', callback);
// ...
server.removeListener('connection', callback); 

removeListener() 最多会从监听器数组中移除一个监听器实例。如果任何一个监听器被多次添加到指定事件名的监听器数组中,则必须多次调用 removeListener() 才能移除每个实例。

事件发出后,在事件发出时附加到事件上的所有监听器都会被依次调用。这意味着,在事件发生后、最后一个监听器执行完毕前,任何 removeListener() 或 removeAllListeners() 调用都不会将它们从正在进行的 emit() 中移除。后续事件的行为与预期一致。

import { EventEmitter } from 'node:events';
class MyEmitter extends EventEmitter {}
const myEmitter = new MyEmitter();const callbackA = () => {console.log('A');myEmitter.removeListener('event', callbackB);
};const callbackB = () => {console.log('B');
};myEmitter.on('event', callbackA);myEmitter.on('event', callbackB);// callbackA removes listener callbackB but it will still be called.
// Internal listener array at time of emit [callbackA, callbackB]
myEmitter.emit('event');
// Prints:
//   A
//   B// callbackB is now removed.
// Internal listener array [callbackA]
myEmitter.emit('event');
// Prints:
//   A

由于监听器是使用内部数组管理的,因此调用此方法将改变在移除监听器后注册的任何监听器的位置索引。这不会影响监听器的调用顺序,但意味着需要重新创建 emitter.listeners() 方法返回的监听器数组副本。
当一个函数被多次添加为一个事件的处理程序时(如下面的示例),removeListener() 将移除最近添加的实例。在该示例中,once(‘ping’) 监听器被移除:

import { EventEmitter } from 'node:events';
const ee = new EventEmitter();function pong() {console.log('pong');
}ee.on('ping', pong);
ee.once('ping', pong);
ee.removeListener('ping', pong);ee.emit('ping');
ee.emit('ping');

返回对 EventEmitter 的引用,以便进行链式调用。

emitter.setMaxListeners(n) 设置最大监听器值

默认情况下,如果为某个特定事件添加的监听器超过 10 个,EventEmitters 将打印警告。这是一个有用的默认设置,有助于发现内存泄漏。通过 emitter.setMaxListeners() 方法,可以修改此特定 EventEmitter 实例的限制。该值可设置为无限(或 0),以表示监听器数量不受限制。
返回对 EventEmitter 的引用,以便进行链式调用。

const ee = new EventEmitter({ captureRejections: true });
for (let index = 0; index < 11; index++) {ee.on("something", async (value) => {console.log(index);});
}
ee.emit("something");

在这里插入图片描述

emitter.rawListeners(eventName) 获取指定侦听器副本

返回名为 eventName 的事件的侦听器数组副本,包括任何封装器(例如由 .once() 创建的封装器)。

import { EventEmitter } from 'node:events';
const emitter = new EventEmitter();
emitter.once('log', () => console.log('log once'));// Returns a new Array with a function `onceWrapper` which has a property
// `listener` which contains the original listener bound above
const listeners = emitter.rawListeners('log');
const logFnWrapper = listeners[0];// Logs "log once" to the console and does not unbind the `once` event
logFnWrapper.listener();// Logs "log once" to the console and removes the listener
logFnWrapper();emitter.on('log', () => console.log('log persistently'));
// Will return a new Array with a single function bound by `.on()` above
const newListeners = emitter.rawListeners('log');// Logs "log persistently" twice
newListeners[0]();
emitter.emit('log');

emitter[Symbol.for(‘nodejs.rejection’)](err, eventName[, …args]) 拒绝承诺函数

Symbol.for(‘nodejs.rejection’) 方法会在发出事件时发生允诺拒绝并在发出者上启用捕获拒绝的情况下被调用。可以使用 events.captureRejectionSymbol 代替 Symbol.for(‘nodejs.rejection’)。

import { EventEmitter, captureRejectionSymbol } from 'node:events';class MyClass extends EventEmitter {constructor() {super({ captureRejections: true });}[captureRejectionSymbol](err, event, ...args) {console.log('rejection happened for', event, 'with', err, ...args);this.destroy(err);}destroy(err) {// Tear the resource down here.}
}

events.defaultMaxListeners 默认最大监听器值

默认情况下,单个事件最多可注册 10 个监听者。
可以使用 emitter.setMaxListeners(n) 方法更改单个 EventEmitter 实例的这一限制。要更改所有 EventEmitter 实例的默认值,可使用 events.defaultMaxListeners 属性。如果该值不是正数,将产生 RangeError 错误。
在设置 events.defaultMaxListeners 时要小心,因为这一更改会影响所有 EventEmitter 实例,包括在更改之前创建的实例。不过,调用 emitter.setMaxListeners(n) 仍优先于 events.defaultMaxListeners。
这并非硬性限制。EventEmitter 实例允许添加更多监听器,但会向 stderr 输出跟踪警告,表明已检测到 “EventEmitter 内存可能泄漏”。对于任何单个 EventEmitter,可以使用 emitter.getMaxListeners() 和 emitter.setMaxListeners() 方法来暂时避免该警告:

import { EventEmitter } from 'node:events';
const emitter = new EventEmitter();
emitter.setMaxListeners(emitter.getMaxListeners() + 1);
emitter.once('event', () => {// do stuffemitter.setMaxListeners(Math.max(emitter.getMaxListeners() - 1, 0));
});

可以使用 --trace-warnings 命令行标志来显示此类警告的堆栈跟踪。
发出的警告可通过 process.on(‘warning’) 查看,并将具有额外的发射器、类型和计数属性,分别指事件发射器实例、事件名称和附加监听器的数量。其 name 属性设置为 “MaxListenersExceededWarning”。

events.errorMonitor 监听错误监听器

该符号用于安装仅用于监控 "错误 "事件的监听器。使用此符号安装的监听器会在调用常规 "错误 "监听器之前被调用。
使用此符号安装监听器不会改变 "错误 "事件发生后的行为。因此,如果没有安装常规的 "错误 "监听器,进程仍会崩溃。

events.getEventListeners(emitterOrTarget, eventName) 获取指定事件监听器数组副本

返回名为 eventName 的事件的侦听器数组副本。
对于事件发射器,这与在发射器上调用 .listeners 的行为完全相同。
对于事件目标,这是获取事件目标的事件侦听器的唯一方法。这对调试和诊断非常有用。

import { getEventListeners, EventEmitter } from 'node:events';{const ee = new EventEmitter();const listener = () => console.log('Events are fun');ee.on('foo', listener);console.log(getEventListeners(ee, 'foo')); // [ [Function: listener] ]
}
{const et = new EventTarget();const listener = () => console.log('Events are fun');et.addEventListener('foo', listener);console.log(getEventListeners(et, 'foo')); // [ [Function: listener] ]
}

events.getMaxListeners(emitterOrTarget) 获取当前监听器最大数量

返回当前设置的最大监听器数量。
对于 EventEmitters,这与在发射器上调用 .getMaxListeners 的行为完全相同。
对于事件目标,这是获取事件目标最大事件侦听器的唯一方法。如果单个 EventTarget 上的事件处理程序数量超过了所设置的最大值,EventTarget 将打印警告。

import { getMaxListeners, setMaxListeners, EventEmitter } from 'node:events';{const ee = new EventEmitter();console.log(getMaxListeners(ee)); // 10setMaxListeners(11, ee);console.log(getMaxListeners(ee)); // 11
}
{const et = new EventTarget();console.log(getMaxListeners(et)); // 10setMaxListeners(11, et);console.log(getMaxListeners(et)); // 11
}

events.once(emitter, name[, options]) 创建监听emitter错误的promise

创建一个 Promise,当 EventEmitter 发出给定事件时,该 Promise 将被执行;如果 EventEmitter 在等待期间发出 “error”,该 Promise 将被拒绝。该 Promise 将以数组的形式解析给定事件的所有参数。

该方法有意采用通用方法,与网络平台 EventTarget 接口配合使用,后者没有特殊的 "错误 "事件语义,也不监听 "错误 "事件。

import { once, EventEmitter } from 'node:events';
import process from 'node:process';const ee = new EventEmitter();process.nextTick(() => {ee.emit('myevent', 42);
});const [value] = await once(ee, 'myevent');
console.log(value);const err = new Error('kaboom');
process.nextTick(() => {ee.emit('error', err);
});try {await once(ee, 'myevent');
} catch (err) {console.error('error happened', err);
}

对 "error"事件的特殊处理仅在使用 events.once() 等待其他事件时才会使用。如果 events.once() 被用来等待 "错误 "事件本身,那么它就会被当作任何其他类型的事件来处理,而不会被特殊处理:

import { EventEmitter, once } from 'node:events';const ee = new EventEmitter();once(ee, 'error').then(([err]) => console.log('ok', err.message)).catch((err) => console.error('error', err.message));ee.emit('error', new Error('boom'));// Prints: ok boom

可以使用 <AbortSignal> 取消对事件的等待:

import { EventEmitter, once } from 'node:events';const ee = new EventEmitter();
const ac = new AbortController();async function foo(emitter, event, signal) {try {await once(emitter, event, { signal });console.log('event emitted!');} catch (error) {if (error.name === 'AbortError') {console.error('Waiting for the event was canceled!');} else {console.error('There was an error', error.message);}}
}foo(ee, 'foo', ac.signal);
ac.abort(); // Abort waiting for the event
ee.emit('foo'); // Prints: Waiting for the event was canceled!

Awaiting multiple events emitted on process.nextTick() 等待 process.nextTick() 发出的多个事件

在使用 events.once() 函数等待同一批 process.nextTick() 操作中发出的多个事件时,或在同步发出多个事件时,有一种边缘情况值得注意。
具体来说,由于 process.nextTick() 队列会在 Promise 微任务队列之前耗尽,而且 EventEmitter 会同步发出所有事件,因此 events.once() 有可能错过一个事件。

import { EventEmitter, once } from 'node:events';
import process from 'node:process';const myEE = new EventEmitter();async function foo() {await once(myEE, 'bar');console.log('bar');// This Promise will never resolve because the 'foo' event will// have already been emitted before the Promise is created.// 无法触发,因为在创建promise之前已经触发foo事件await once(myEE, 'foo');console.log('foo');
}process.nextTick(() => {myEE.emit('bar');myEE.emit('foo');
});foo().then(() => console.log('done'));

要捕捉这两个事件,应在等待其中任何一个事件之前创建每个 Promise,然后就可以使用 Promise.all()、Promise.race() 或 Promise.allSettled():

import { EventEmitter, once } from 'node:events';
import process from 'node:process';const myEE = new EventEmitter();async function foo() {await Promise.all([once(myEE, 'bar'), once(myEE, 'foo')]);console.log('foo', 'bar');
}process.nextTick(() => {myEE.emit('bar');myEE.emit('foo');
});foo().then(() => console.log('done'));

events.captureRejections 全局 captureRejections 选项

更改所有新 EventEmitter 对象的默认 captureRejections 选项。

events.captureRejectionSymbol

值:Symbol.for(‘nodejs.rejection’)

events.listenerCount(emitter, eventName)

类方法,用于返回在给定发射器上注册的给定事件名的监听者数量。

已弃用:使用 emitter.listenerCount() 代替。

events.on(emitter, eventName[, options]) 用于迭代由发射器发射的 eventName 事件

参数:

  • emitter —— 发射器 <EventEmitter>
  • eventName —— 正在监听的事件名称。
  • options —— 配置对象
    • signal —— 可用于取消等待事件。
    • close —— 将结束迭代的事件名称。
    • highWaterMark —— 默认值:Number.MAX_SAFE_INTEGER 高水印。每当缓冲事件的大小大于该值时,发射器就会暂停。仅支持执行了 pause() 和 resume() 方法的发射器。
      -lowWaterMark —— 默认值:1 低水印。每当缓冲事件的大小低于该值时,发射器就会恢复。仅支持执行了 pause() 和 resume() 方法的发射器。
  • 返回<AsyncIterator>,迭代由发射器发射的 eventName 事件
import { on, EventEmitter } from 'node:events';
import process from 'node:process';const ee = new EventEmitter();// Emit later on
process.nextTick(() => {ee.emit('foo', 'bar');ee.emit('foo', 42);
});for await (const event of on(ee, 'foo')) {// The execution of this inner block is synchronous and it// processes one event at a time (even with await). Do not use// if concurrent execution is required.console.log(event); // prints ['bar'] [42]
}
// Unreachable here

返回迭代 eventName 事件的 AsyncIterator。如果 EventEmitter 发出 “错误”,它将抛出。退出循环时,它会删除所有监听器。每次迭代返回的值是一个由已发出的事件参数组成的数组。

<AbortSignal> 可用于取消对事件的等待:

import { on, EventEmitter } from 'node:events';
import process from 'node:process';const ac = new AbortController();(async () => {const ee = new EventEmitter();// Emit later onprocess.nextTick(() => {ee.emit('foo', 'bar');ee.emit('foo', 42);});for await (const event of on(ee, 'foo', { signal: ac.signal })) {// The execution of this inner block is synchronous and it// processes one event at a time (even with await). Do not use// if concurrent execution is required.console.log(event); // prints ['bar'] [42]}// Unreachable here
})();process.nextTick(() => ac.abort());

events.setMaxListeners(n[, …eventTargets]) 设置最大监听器数量

设置最大指定监听器的数量,eventTargets没有则 n 将被设置为所有新创建的 <EventTarget> 和 <EventEmitter> 对象的默认最大值。

Class: events.EventEmitterAsyncResource extends EventEmitter

将 EventEmitter 与 <AsyncResource> 集成,用于需要手动异步跟踪的 EventEmitter。具体来说,由 events.EventEmitterAsyncResource 实例发出的所有事件都将在其异步上下文中运行。

import { EventEmitterAsyncResource, EventEmitter } from "node:events";
import { notStrictEqual, strictEqual } from "node:assert";
import { executionAsyncId, triggerAsyncId } from "node:async_hooks";// 异步跟踪工具会将其标识为“Q”。
const ee1 = new EventEmitterAsyncResource({ name: "Q" });// 'foo'监听器将在eventemitter异步上下文中运行。
ee1.on("foo", () => {strictEqual(executionAsyncId(), ee1.asyncId);console.log(executionAsyncId(), triggerAsyncId());strictEqual(triggerAsyncId(), ee1.triggerAsyncId);
});const ee2 = new EventEmitter();//  对于不跟踪异步上下文的普通 EventEmitter 上的 'foo' 监听器,其运行在与 emit() 相同的异步上下文中。
ee2.on("foo", () => {notStrictEqual(executionAsyncId(), ee2.asyncId);notStrictEqual(triggerAsyncId(), ee2.triggerAsyncId);console.log(executionAsyncId(), triggerAsyncId());
});Promise.resolve().then(() => {ee1.emit("foo");ee2.emit("foo");
});

EventEmitterAsyncResource 类的方法和选项与 EventEmitter 和 AsyncResource 本身相同。

new events.EventEmitterAsyncResource([options]) 创建EventEmitterAsyncResource实例

参数:

  • 选项
    • captureRejections <布尔> 启用自动捕获承诺拒绝。默认值:false。
    • name <string> 异步事件的类型。默认:new.target.name。
    • triggerAsyncId <number> 创建此异步事件的执行上下文的 ID。默认: executionAsyncId()。
    • requireManualDestroy <boolean>如果设置为 “true”,则在对象被垃圾回收时禁用 emitDestroy。通常不需要设置此项(即使手动调用了 emitDestroy),除非检索到资源的 asyncId 并调用了敏感 API 的 emitDestroy。当设置为 false 时,只有在至少有一个活动的 destroy 钩子时,才会在垃圾回收时调用 emitDestroy。默认值:false。

eventemitterasyncresource.asyncId 分配给资源的唯一 asyncId。

分配给资源的唯一 asyncId。

eventemitterasyncresource.asyncResource 底层 <AsyncResource>。

返回的 AsyncResource 对象有一个额外的 eventEmitter 属性,该属性提供了对该 EventEmitterAsyncResource 的引用。

eventemitterasyncresource.emitDestroy() 销毁事件

调用所有销毁钩子。只能调用一次。如果调用超过一次,将产生错误。必须手动调用。如果资源被留待 GC 收集,则销毁钩子将永远不会被调用。

eventemitterasyncresource.triggerAsyncId

与传递给 AsyncResource 构造函数的 triggerAsyncId 相同。

EventTarget and Event API

EventTarget 和 Event 对象是一些 Node.js 核心 API 所公开的 EventTarget Web API 的特定 Node.js 实现。

const target = new EventTarget();target.addEventListener('foo', (event) => {console.log('foo event happened!');
}); 

Node.js EventTarget vs. DOM EventTarget

Node.js EventTarget 和 EventTarget Web API 之间有两个主要区别:

  1. DOM EventTarget 实例可能是分层的,而 Node.js 中没有分层和事件传播的概念。也就是说,派发到 EventTarget 的事件不会通过嵌套目标对象的层次结构传播,这些嵌套目标对象可能各自有自己的事件处理程序集。
  2. 在 Node.js EventTarget 中,如果事件监听器是一个异步函数或返回一个 Promise,并且返回的 Promise 发生拒绝,则会自动捕获拒绝,并以与同步抛出的监听器相同的方式进行处理(详见 EventTarget 错误处理)。

NodeEventTarget 比较 EventEmitter

NodeEventTarget 对象实现了 EventEmitter API 的一个修改子集,可在某些情况下近似模拟 EventEmitter。NodeEventTarget 不是 EventEmitter 的实例,在大多数情况下不能代替 EventEmitter 使用。

  1. 与 EventEmitter 不同,每个事件类型的监听器最多只能注册一次。多次注册监听器的尝试将被忽略。
  2. NodeEventTarget 并不模拟完整的 EventEmitter API。具体来说,它没有模拟 prependListener()、prependOnceListener()、rawListeners() 和 errorMonitor API。newListener "和 "removeListener "事件也不会发出。
  3. 对于类型为 "error "的事件,NodeEventTarget 没有实现任何特殊的默认行为。
  4. NodeEventTarget 支持将 EventListener 对象和函数作为所有事件类型的处理程序。

Event listener 事件监听器

为事件类型注册的事件监听器可以是 JavaScript 函数,也可以是带有 handleEvent 属性(其值为函数)的对象。
无论哪种情况,处理程序函数都会调用传递给 eventTarget.dispatchEvent() 函数的事件参数。

异步函数可用作事件监听器。如果异步处理函数拒绝接受,则会捕获拒绝并按照 EventTarget 错误处理中的描述进行处理。

一个处理函数抛出的错误不会阻止其他处理函数被调用。
处理函数的返回值将被忽略。
处理程序总是按照添加的顺序调用。
处理函数可以更改事件对象。

function handler1(event) {console.log(event.type);  // Prints 'foo'event.a = 1;
}async function handler2(event) {console.log(event.type);  // Prints 'foo'console.log(event.a);  // Prints 1
}const handler3 = {handleEvent(event) {console.log(event.type);  // Prints 'foo'},
};const handler4 = {async handleEvent(event) {console.log(event.type);  // Prints 'foo'},
};const target = new EventTarget();target.addEventListener('foo', handler1);
target.addEventListener('foo', handler2);
target.addEventListener('foo', handler3);
target.addEventListener('foo', handler4, { once: true }); 

EventTarget error handling EventTarget 错误处理

当注册的事件监听器抛出(或返回拒绝的 Promise)时,默认情况下,该错误会在 process.nextTick() 中被视为未捕获异常。这意味着事件目标中的未捕获异常将默认终止 Node.js 进程。
在事件监听器中抛出不会阻止其他已注册处理程序被调用。
EventTarget 并没有像 EventEmitter 一样,为 "错误 "类型事件实现任何特殊的默认处理方式。
目前,错误会先转发到 process.on(‘error’) 事件,然后再转发到 process.on(‘uncaughtException’)。这一行为已被弃用,并将在未来的版本中进行修改,以使 EventTarget 与其他 Node.js API 保持一致。任何依赖 process.on(‘error’) 事件的代码都应与新行为保持一致。

Class: Event Event类

事件对象是对事件 Web API 的改编。实例由 Node.js 在内部创建。

event.bubbles

Node.js 中不使用此功能,提供此功能纯粹是为了完整。

event.cancelBubble

如果设置为 true,则是 event.stopPropagation() 的别名。Node.js 中不使用此函数,提供此函数纯粹是为了完整。

event.cancelable

如果创建事件时使用了可取消选项,则为 True。

event.composed

Node.js 中不使用此功能,提供此功能纯粹是为了完整。

event.composedPath()

返回一个数组,其中唯一的条目是当前事件目标(EventTarget),如果事件未被派发,则返回空数组。Node.js 中不使用此参数,提供此参数纯粹是为了完整。

event.currentTarget

event.target 的别名。

event.defaultPrevented

如果 cancelable 为 true 且调用了 event.preventDefault(),则为 true。

event.eventPhase

Node.js 中不使用此功能,提供此功能纯粹是为了完整。

event.initEvent(type[, bubbles[, cancelable]])

与事件构造函数冗余,无法设置组成。Node.js 中未使用此功能,提供此功能纯粹是为了完整。

event.isTrusted

<AbortSignal> "中止 "事件在 isTrusted 设置为 true 时发出。在所有其他情况下,该值为 false。

event.preventDefault()

如果可取消为 true,则将 defaultPrevented 属性设置为 true。

event.returnValue

event.returnValue 的值总是与 event.defaultPrevented 相反。Node.js 中不使用此值,提供此值纯粹是为了完整。

event.srcElement

event.target 的别名。

遗留问题:使用 event.target 代替。

event.stopImmediatePropagation()

Node.js 中不使用此功能,提供此功能纯粹是为了完整。

event.stopPropagation()

Node.js 中不使用此功能,提供此功能纯粹是为了完整。

event.target

调度事件的 EventTarget。

event.timeStamp

事件对象创建时的毫秒时间戳。

event.type

事件类型标识符。

Class: EventTarget

eventTarget.addEventListener(type, listener[, options])

参数:

  • type —— 字符串
  • listener<函数> | <事件监听器>
  • options<对象
    • once <布尔值> 为 true 时,监听器会在首次调用时自动移除。默认值:false。
    • passive <boolean> 为真时,提示监听器不会调用事件对象的 preventDefault() 方法。默认值:false。
    • capture <布尔> Node.js 不直接使用。为使 API 更完整而添加。默认值:false。
    • signal <AbortSignal> 当调用给定 AbortSignal 对象的 abort() 方法时,监听器将被移除。

为类型事件添加新的处理程序。每个类型和每个捕获选项值只能添加一次监听器。
如果 once 选项为 “true”,监听器将在下一次类型事件派发后移除。
除了根据 EventTarget 规范跟踪已注册的事件监听器外,Node.js 不会以任何功能方式使用捕获选项。具体来说,捕获选项在注册监听器时被用作密钥的一部分。任何一个监听器都可以在 capture = false 和 capture = true 时分别添加一次。

function handler(event) {}const target = new EventTarget();
target.addEventListener('foo', handler, { capture: true });  // first
target.addEventListener('foo', handler, { capture: false }); // second// Removes the second instance of handler
target.removeEventListener('foo', handler);// Removes the first instance of handler
target.removeEventListener('foo', handler, { capture: true }); 

eventTarget.dispatchEvent(event)

将事件分派给事件类型的处理程序列表。
已注册的事件监听器会按照注册顺序被同步调用。

eventTarget.removeEventListener(type, listener[, options])

从事件类型的处理程序列表中删除监听器。

Class: CustomEvent

CustomEvent 对象是对 CustomEvent Web API 的改编。实例由 Node.js 在内部创建。

event.detail

返回初始化时传递的自定义数据。

Class: NodeEventTarget

NodeEventTarget 是 EventTarget 的 Node.js 特定扩展,它模拟了 EventEmitter API 的一个子集。

nodeEventTarget.addListener(type, listener) 添加

事件目标(EventTarget)类的 Node.js 特定扩展,可模拟等效的 EventEmitter API。addListener() 和 addEventListener() 之间的唯一区别是,addListener() 将返回对 EventTarget 的引用。

nodeEventTarget.emit(type, arg) 触发

事件目标(EventTarget)类的 Node.js 特定扩展,用于将 arg 发送到类型的处理程序列表。

nodeEventTarget.eventNames()

事件目标(EventTarget)类的 Node.js 特定扩展,用于返回已注册事件监听器的事件类型名称数组。

nodeEventTarget.listenerCount(type)

EventTarget 类的 Node.js 特定扩展,用于返回为该类型注册的事件监听器数量。

nodeEventTarget.setMaxListeners(n)

事件目标(EventTarget)类的 Node.js 特定扩展,可将最大事件侦听器数量设置为 n。

nodeEventTarget.getMaxListeners()

事件目标(EventTarget)类的 Node.js 特定扩展,用于返回最大事件侦听器的数量。

nodeEventTarget.off(type, listener[, options])

eventTarget.removeEventListener() 的 Node.js 专用别名。

nodeEventTarget.on(type, listener)

eventTarget.addEventListener() 的 Node.js 专用别名。

nodeEventTarget.once(type, listener)

事件目标(EventTarget)类的 Node.js 特定扩展,可为给定的事件类型添加一次监听器。这相当于在调用 on 时将 once 选项设为 true。

nodeEventTarget.removeAllListeners([type])

事件目标(EventTarget)类的 Node.js 特定扩展。如果指定了类型,则移除该类型的所有已注册侦听器,否则移除所有已注册侦听器。

nodeEventTarget.removeListener(type, listener[, options])

Node.js 对 EventTarget 类的特定扩展,用于移除给定类型的监听器。removeListener() 与 removeEventListener() 的唯一区别是,removeListener() 将返回对 EventTarget 的引用。

结语

结束了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/340494.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神经网络是什么?有什么作用?

人工智能是当前的热门科技领域&#xff0c;在自动驾驶、金融服务、智能家居、零售和电商、工业制造、医疗领域、教育领域、交通领域、娱乐领域、能源管理、农业、航空航天等很多领域都有越来越多的应用。 发展人工智能&#xff0c;离不开算力&#xff08;芯片&#xff09;、算…

单实例11.2.0.4迁移到11.2.0.4RAC_使用rman异机恢复

保命法则&#xff1a;先备份再操作&#xff0c;磁盘空间紧张无法备份就让满足&#xff0c;给自己留退路。 场景说明&#xff1a; 1.本文档的环境为同平台、不同版本&#xff08;操作系统版本可以不同&#xff0c;数据库版本相同&#xff09;&#xff0c;源机器和目标机器部分…

语雀使用指南

语雀使用指南 语雀什么是语雀注册知识库新建文档新建表格 使用感受 语雀 在介绍语雀之前&#xff0c;首先来说一下什么是语雀&#xff1f; 什么是语雀 语雀的官方文档定义是这么说的&#xff1a;「语雀」是一个「专业的云端知识库」&#xff0c;孵化自 蚂蚁集团 &#xff0c…

迎七一党史知识竞赛答题怎么做

迎七一党史知识竞赛答题&#xff0c;不仅是对于党史知识的检验&#xff0c;更是对于参赛者学习态度和综合能力的考量。在参与这类竞赛时&#xff0c;我们需要做好充分的准备&#xff0c;掌握一定的答题技巧&#xff0c;才能取得好的成绩。 首先&#xff0c;我们要深入了解竞赛…

【LeetCode】38.外观数列

外观数列 题目描述&#xff1a; 「外观数列」是一个数位字符串序列&#xff0c;由递归公式定义&#xff1a; countAndSay(1) "1"countAndSay(n) 是 countAndSay(n-1) 的行程长度编码。 行程长度编码&#xff08;RLE&#xff09;是一种字符串压缩方法&#xff0c…

【python】修改目标检测的xml标签(VOC)类别名

需求&#xff1a; 在集成多个数据集一同训练时&#xff0c;可能会存在不同数据集针对同一种目标有不同的类名&#xff0c;可以通过python脚本修改数据内的类名映射&#xff0c;实现统一数据集标签名的目的。 代码&#xff1a; # -*- coding: utf-8 -*- # Time : 2023/9/11 1…

SOLIDWORKS教育版:提供学生所需的资源

SOLIDWORKS教育版是一款专为学生和教育工作者设计的3D CAD软件&#xff0c;它拥有强大的设计功能和用户友好的界面&#xff0c;让你轻松实现创新设计的梦想。这款软件不仅提供了丰富的教程和案例&#xff0c;还为学生提供了各种学习资源和支持&#xff0c;让你在学习的道路上不…

Python程序设计 身份证号的奥秘

第1关&#xff1a;判断性别 通过身份证的第17位也就是倒数第二位的数字可以辨别该身份证所属人的性别,奇数为男性,偶数为女性。 任务&#xff1a;输入身份证号&#xff0c;第17位若是偶数&#xff0c;输出男性&#xff0c;否则输出女性 如何截取字符串的一个字符 如何判断一个…

数据持久化第七课-URL重写与Ajax

数据持久化第七课-URL重写与Ajax 一.预习笔记 1.URL重写(对网页地址进行保护) 首先编写module,实现对网络地址的处理 其次就是module的配置 最后验证url重写技术 2.Ajax数据交互 编写后端响应数据 处理跨域的配置问题 运行项目得到后端响应数据的地址 编写前端ajax进行数据请…

【Python】 如何优雅地终止 Python 中的线程

基本原理 在 Python 中&#xff0c;线程&#xff08;Thread&#xff09;是一种执行并行计算的基本单位。然而&#xff0c;有时候我们需要在特定条件下终止一个正在运行的线程。Python 的标准库 threading 并没有提供直接终止线程的方法&#xff0c;因为强制终止线程可能会导致…

AC 800PEC 高性能控制系统GFD563A101 3BHE046836R0101

AC 800PEC 控制系统对于大功率整流器应用具有极快控制算法的高性能应用 –快速控制的周期时间范围为100 μs(微秒) 长期操作瞬变的循环至秒-要求专用控制设备。这就是为什么我们设计了AC 800PEC&#xff0c;扩展ABB著名的自动化技术来处理高速电力电子等过程的算法应用程序。 …

github有趣项目:Verilog在线仿真( DigitalJS+edaplayground)

DigitalJS https://github.com/tilk/digitaljs这个项目是一个用Javascript实现的数字电路模拟器。 它旨在模拟由硬件设计工具合成的电路 像 Yosys&#xff08;这里是 Github 存储库&#xff09;&#xff0c;它有一个配套项目 yosys2digitaljs&#xff0c;它可以转换 Yosys 将文…

汽车IVI中控开发入门及进阶(二十四):杰发科技AC8015

前言: 在此之前的大部分时间,四维图新更多的是以图商的身份在业内出现,但现在四维图新图商之外的技术积累提现在了杰发科技身上,或者是从图商到汽车智能化一体解决方案供应商的角色转变。汽车智能化,可以简单的归为座舱智能化和智能驾驶两个板块。 随着汽车变得越来越智能…

04.docker的主要组成部分

docker体验 docker是传统的CS架构分为docker client和docker server,跟mysql一样 查看版本命令&#xff1a;docker version 查看docker下载的是社区版,ce代表社区 rpm -qa |grep docker 查看docker系统命令 docker system docker info&#xff08;如果要做监控&#xff…

13. 《C语言》——【strlen函数的使用和模拟实现】

文章目录 前言strlen函数strlen函数的使用strlen函数的3种方法实现方法1方法2方法3 总结 前言 各位老板好~ &#xff0c; 今天我们讲解strlen函数如何去使用以及如何去模拟实现strlen函数。希望各位老板能够给一个点赞和一个大大的关注&#xff0c;感谢各位老板&#xff01;str…

【翻译软件】CopyTranslator复制即翻译的外文辅助阅读翻译软件NO.102

使用平台&#xff1a;Windows/Linux/macOS 设置里选择翻译引擎和翻译API&#xff0c;谷歌翻译已经退出中国了&#xff0c;但还是提供了镜像地址 一、复制即翻译 只需要复制文本到剪贴板&#xff0c;就可以查看翻译结果 记得开启“自动粘贴”哦。 二、多段同时翻译 三、智能…

第二证券炒股知识:短线炒股技巧?

在股票商场上&#xff0c;出资者分为长线和短线这两大类&#xff0c;其中短线炒股存在以下技巧&#xff1a; 1、早盘集合竞价时刻上的技巧 早上集合竞价对短线出资者来说比较重要&#xff0c;其中早上集合竞价期间9&#xff1a;15-9:20之间出资者可以进行撤单操作&#xff0c…

新项目来了,JDK 17和JDK 21 该如何选择?

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

【机器学习数据挖掘】基于ARIMA 自回归积分滑动平均模型的销售价格库存分析报告 附完整python代码

资源地址&#xff1a;Python数据分析大作业 4000字 图文分析文档 销售分析 完整python代码 ​ 完整代码分析 同时销售量后1000的sku品类占比中&#xff08;不畅销产品&#xff09;如上&#xff0c;精品类产品占比第一&#xff0c;达到66.7%&#xff0c;其次是香化类产品&#…

【mysql】ssl_choose_client_version:unsupported protocol

起因&#xff1a;项目上的DolphinScheduler连接不上数据库&#xff0c;查看worker日志提到SSL协议问题&#xff1a; com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failureCaused by: java.io.EOFException: SSL peer shut down incorrectly 我…