socket.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902
  1. import { PacketType } from "socket.io-parser";
  2. import { on } from "./on.js";
  3. import { Emitter, } from "@socket.io/component-emitter";
  4. import debugModule from "debug"; // debug()
  5. const debug = debugModule("socket.io-client:socket"); // debug()
  6. /**
  7. * Internal events.
  8. * These events can't be emitted by the user.
  9. */
  10. const RESERVED_EVENTS = Object.freeze({
  11. connect: 1,
  12. connect_error: 1,
  13. disconnect: 1,
  14. disconnecting: 1,
  15. // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener
  16. newListener: 1,
  17. removeListener: 1,
  18. });
  19. /**
  20. * A Socket is the fundamental class for interacting with the server.
  21. *
  22. * A Socket belongs to a certain Namespace (by default /) and uses an underlying {@link Manager} to communicate.
  23. *
  24. * @example
  25. * const socket = io();
  26. *
  27. * socket.on("connect", () => {
  28. * console.log("connected");
  29. * });
  30. *
  31. * // send an event to the server
  32. * socket.emit("foo", "bar");
  33. *
  34. * socket.on("foobar", () => {
  35. * // an event was received from the server
  36. * });
  37. *
  38. * // upon disconnection
  39. * socket.on("disconnect", (reason) => {
  40. * console.log(`disconnected due to ${reason}`);
  41. * });
  42. */
  43. export class Socket extends Emitter {
  44. /**
  45. * `Socket` constructor.
  46. */
  47. constructor(io, nsp, opts) {
  48. super();
  49. /**
  50. * Whether the socket is currently connected to the server.
  51. *
  52. * @example
  53. * const socket = io();
  54. *
  55. * socket.on("connect", () => {
  56. * console.log(socket.connected); // true
  57. * });
  58. *
  59. * socket.on("disconnect", () => {
  60. * console.log(socket.connected); // false
  61. * });
  62. */
  63. this.connected = false;
  64. /**
  65. * Whether the connection state was recovered after a temporary disconnection. In that case, any missed packets will
  66. * be transmitted by the server.
  67. */
  68. this.recovered = false;
  69. /**
  70. * Buffer for packets received before the CONNECT packet
  71. */
  72. this.receiveBuffer = [];
  73. /**
  74. * Buffer for packets that will be sent once the socket is connected
  75. */
  76. this.sendBuffer = [];
  77. /**
  78. * The queue of packets to be sent with retry in case of failure.
  79. *
  80. * Packets are sent one by one, each waiting for the server acknowledgement, in order to guarantee the delivery order.
  81. * @private
  82. */
  83. this._queue = [];
  84. /**
  85. * A sequence to generate the ID of the {@link QueuedPacket}.
  86. * @private
  87. */
  88. this._queueSeq = 0;
  89. this.ids = 0;
  90. /**
  91. * A map containing acknowledgement handlers.
  92. *
  93. * The `withError` attribute is used to differentiate handlers that accept an error as first argument:
  94. *
  95. * - `socket.emit("test", (err, value) => { ... })` with `ackTimeout` option
  96. * - `socket.timeout(5000).emit("test", (err, value) => { ... })`
  97. * - `const value = await socket.emitWithAck("test")`
  98. *
  99. * From those that don't:
  100. *
  101. * - `socket.emit("test", (value) => { ... });`
  102. *
  103. * In the first case, the handlers will be called with an error when:
  104. *
  105. * - the timeout is reached
  106. * - the socket gets disconnected
  107. *
  108. * In the second case, the handlers will be simply discarded upon disconnection, since the client will never receive
  109. * an acknowledgement from the server.
  110. *
  111. * @private
  112. */
  113. this.acks = {};
  114. this.flags = {};
  115. this.io = io;
  116. this.nsp = nsp;
  117. if (opts && opts.auth) {
  118. this.auth = opts.auth;
  119. }
  120. this._opts = Object.assign({}, opts);
  121. if (this.io._autoConnect)
  122. this.open();
  123. }
  124. /**
  125. * Whether the socket is currently disconnected
  126. *
  127. * @example
  128. * const socket = io();
  129. *
  130. * socket.on("connect", () => {
  131. * console.log(socket.disconnected); // false
  132. * });
  133. *
  134. * socket.on("disconnect", () => {
  135. * console.log(socket.disconnected); // true
  136. * });
  137. */
  138. get disconnected() {
  139. return !this.connected;
  140. }
  141. /**
  142. * Subscribe to open, close and packet events
  143. *
  144. * @private
  145. */
  146. subEvents() {
  147. if (this.subs)
  148. return;
  149. const io = this.io;
  150. this.subs = [
  151. on(io, "open", this.onopen.bind(this)),
  152. on(io, "packet", this.onpacket.bind(this)),
  153. on(io, "error", this.onerror.bind(this)),
  154. on(io, "close", this.onclose.bind(this)),
  155. ];
  156. }
  157. /**
  158. * Whether the Socket will try to reconnect when its Manager connects or reconnects.
  159. *
  160. * @example
  161. * const socket = io();
  162. *
  163. * console.log(socket.active); // true
  164. *
  165. * socket.on("disconnect", (reason) => {
  166. * if (reason === "io server disconnect") {
  167. * // the disconnection was initiated by the server, you need to manually reconnect
  168. * console.log(socket.active); // false
  169. * }
  170. * // else the socket will automatically try to reconnect
  171. * console.log(socket.active); // true
  172. * });
  173. */
  174. get active() {
  175. return !!this.subs;
  176. }
  177. /**
  178. * "Opens" the socket.
  179. *
  180. * @example
  181. * const socket = io({
  182. * autoConnect: false
  183. * });
  184. *
  185. * socket.connect();
  186. */
  187. connect() {
  188. if (this.connected)
  189. return this;
  190. this.subEvents();
  191. if (!this.io["_reconnecting"])
  192. this.io.open(); // ensure open
  193. if ("open" === this.io._readyState)
  194. this.onopen();
  195. return this;
  196. }
  197. /**
  198. * Alias for {@link connect()}.
  199. */
  200. open() {
  201. return this.connect();
  202. }
  203. /**
  204. * Sends a `message` event.
  205. *
  206. * This method mimics the WebSocket.send() method.
  207. *
  208. * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
  209. *
  210. * @example
  211. * socket.send("hello");
  212. *
  213. * // this is equivalent to
  214. * socket.emit("message", "hello");
  215. *
  216. * @return self
  217. */
  218. send(...args) {
  219. args.unshift("message");
  220. this.emit.apply(this, args);
  221. return this;
  222. }
  223. /**
  224. * Override `emit`.
  225. * If the event is in `events`, it's emitted normally.
  226. *
  227. * @example
  228. * socket.emit("hello", "world");
  229. *
  230. * // all serializable datastructures are supported (no need to call JSON.stringify)
  231. * socket.emit("hello", 1, "2", { 3: ["4"], 5: Uint8Array.from([6]) });
  232. *
  233. * // with an acknowledgement from the server
  234. * socket.emit("hello", "world", (val) => {
  235. * // ...
  236. * });
  237. *
  238. * @return self
  239. */
  240. emit(ev, ...args) {
  241. var _a, _b, _c;
  242. if (RESERVED_EVENTS.hasOwnProperty(ev)) {
  243. throw new Error('"' + ev.toString() + '" is a reserved event name');
  244. }
  245. args.unshift(ev);
  246. if (this._opts.retries && !this.flags.fromQueue && !this.flags.volatile) {
  247. this._addToQueue(args);
  248. return this;
  249. }
  250. const packet = {
  251. type: PacketType.EVENT,
  252. data: args,
  253. };
  254. packet.options = {};
  255. packet.options.compress = this.flags.compress !== false;
  256. // event ack callback
  257. if ("function" === typeof args[args.length - 1]) {
  258. const id = this.ids++;
  259. debug("emitting packet with ack id %d", id);
  260. const ack = args.pop();
  261. this._registerAckCallback(id, ack);
  262. packet.id = id;
  263. }
  264. const isTransportWritable = (_b = (_a = this.io.engine) === null || _a === void 0 ? void 0 : _a.transport) === null || _b === void 0 ? void 0 : _b.writable;
  265. const isConnected = this.connected && !((_c = this.io.engine) === null || _c === void 0 ? void 0 : _c._hasPingExpired());
  266. const discardPacket = this.flags.volatile && !isTransportWritable;
  267. if (discardPacket) {
  268. debug("discard packet as the transport is not currently writable");
  269. }
  270. else if (isConnected) {
  271. this.notifyOutgoingListeners(packet);
  272. this.packet(packet);
  273. }
  274. else {
  275. this.sendBuffer.push(packet);
  276. }
  277. this.flags = {};
  278. return this;
  279. }
  280. /**
  281. * @private
  282. */
  283. _registerAckCallback(id, ack) {
  284. var _a;
  285. const timeout = (_a = this.flags.timeout) !== null && _a !== void 0 ? _a : this._opts.ackTimeout;
  286. if (timeout === undefined) {
  287. this.acks[id] = ack;
  288. return;
  289. }
  290. // @ts-ignore
  291. const timer = this.io.setTimeoutFn(() => {
  292. delete this.acks[id];
  293. for (let i = 0; i < this.sendBuffer.length; i++) {
  294. if (this.sendBuffer[i].id === id) {
  295. debug("removing packet with ack id %d from the buffer", id);
  296. this.sendBuffer.splice(i, 1);
  297. }
  298. }
  299. debug("event with ack id %d has timed out after %d ms", id, timeout);
  300. ack.call(this, new Error("operation has timed out"));
  301. }, timeout);
  302. const fn = (...args) => {
  303. // @ts-ignore
  304. this.io.clearTimeoutFn(timer);
  305. ack.apply(this, args);
  306. };
  307. fn.withError = true;
  308. this.acks[id] = fn;
  309. }
  310. /**
  311. * Emits an event and waits for an acknowledgement
  312. *
  313. * @example
  314. * // without timeout
  315. * const response = await socket.emitWithAck("hello", "world");
  316. *
  317. * // with a specific timeout
  318. * try {
  319. * const response = await socket.timeout(1000).emitWithAck("hello", "world");
  320. * } catch (err) {
  321. * // the server did not acknowledge the event in the given delay
  322. * }
  323. *
  324. * @return a Promise that will be fulfilled when the server acknowledges the event
  325. */
  326. emitWithAck(ev, ...args) {
  327. return new Promise((resolve, reject) => {
  328. const fn = (arg1, arg2) => {
  329. return arg1 ? reject(arg1) : resolve(arg2);
  330. };
  331. fn.withError = true;
  332. args.push(fn);
  333. this.emit(ev, ...args);
  334. });
  335. }
  336. /**
  337. * Add the packet to the queue.
  338. * @param args
  339. * @private
  340. */
  341. _addToQueue(args) {
  342. let ack;
  343. if (typeof args[args.length - 1] === "function") {
  344. ack = args.pop();
  345. }
  346. const packet = {
  347. id: this._queueSeq++,
  348. tryCount: 0,
  349. pending: false,
  350. args,
  351. flags: Object.assign({ fromQueue: true }, this.flags),
  352. };
  353. args.push((err, ...responseArgs) => {
  354. if (packet !== this._queue[0]) {
  355. return debug("packet [%d] already acknowledged", packet.id);
  356. }
  357. const hasError = err !== null;
  358. if (hasError) {
  359. if (packet.tryCount > this._opts.retries) {
  360. debug("packet [%d] is discarded after %d tries", packet.id, packet.tryCount);
  361. this._queue.shift();
  362. if (ack) {
  363. ack(err);
  364. }
  365. }
  366. }
  367. else {
  368. debug("packet [%d] was successfully sent", packet.id);
  369. this._queue.shift();
  370. if (ack) {
  371. ack(null, ...responseArgs);
  372. }
  373. }
  374. packet.pending = false;
  375. return this._drainQueue();
  376. });
  377. this._queue.push(packet);
  378. this._drainQueue();
  379. }
  380. /**
  381. * Send the first packet of the queue, and wait for an acknowledgement from the server.
  382. * @param force - whether to resend a packet that has not been acknowledged yet
  383. *
  384. * @private
  385. */
  386. _drainQueue(force = false) {
  387. debug("draining queue");
  388. if (!this.connected || this._queue.length === 0) {
  389. return;
  390. }
  391. const packet = this._queue[0];
  392. if (packet.pending && !force) {
  393. debug("packet [%d] has already been sent and is waiting for an ack", packet.id);
  394. return;
  395. }
  396. packet.pending = true;
  397. packet.tryCount++;
  398. debug("sending packet [%d] (try n°%d)", packet.id, packet.tryCount);
  399. this.flags = packet.flags;
  400. this.emit.apply(this, packet.args);
  401. }
  402. /**
  403. * Sends a packet.
  404. *
  405. * @param packet
  406. * @private
  407. */
  408. packet(packet) {
  409. packet.nsp = this.nsp;
  410. this.io._packet(packet);
  411. }
  412. /**
  413. * Called upon engine `open`.
  414. *
  415. * @private
  416. */
  417. onopen() {
  418. debug("transport is open - connecting");
  419. if (typeof this.auth == "function") {
  420. this.auth((data) => {
  421. this._sendConnectPacket(data);
  422. });
  423. }
  424. else {
  425. this._sendConnectPacket(this.auth);
  426. }
  427. }
  428. /**
  429. * Sends a CONNECT packet to initiate the Socket.IO session.
  430. *
  431. * @param data
  432. * @private
  433. */
  434. _sendConnectPacket(data) {
  435. this.packet({
  436. type: PacketType.CONNECT,
  437. data: this._pid
  438. ? Object.assign({ pid: this._pid, offset: this._lastOffset }, data)
  439. : data,
  440. });
  441. }
  442. /**
  443. * Called upon engine or manager `error`.
  444. *
  445. * @param err
  446. * @private
  447. */
  448. onerror(err) {
  449. if (!this.connected) {
  450. this.emitReserved("connect_error", err);
  451. }
  452. }
  453. /**
  454. * Called upon engine `close`.
  455. *
  456. * @param reason
  457. * @param description
  458. * @private
  459. */
  460. onclose(reason, description) {
  461. debug("close (%s)", reason);
  462. this.connected = false;
  463. delete this.id;
  464. this.emitReserved("disconnect", reason, description);
  465. this._clearAcks();
  466. }
  467. /**
  468. * Clears the acknowledgement handlers upon disconnection, since the client will never receive an acknowledgement from
  469. * the server.
  470. *
  471. * @private
  472. */
  473. _clearAcks() {
  474. Object.keys(this.acks).forEach((id) => {
  475. const isBuffered = this.sendBuffer.some((packet) => String(packet.id) === id);
  476. if (!isBuffered) {
  477. // note: handlers that do not accept an error as first argument are ignored here
  478. const ack = this.acks[id];
  479. delete this.acks[id];
  480. if (ack.withError) {
  481. ack.call(this, new Error("socket has been disconnected"));
  482. }
  483. }
  484. });
  485. }
  486. /**
  487. * Called with socket packet.
  488. *
  489. * @param packet
  490. * @private
  491. */
  492. onpacket(packet) {
  493. const sameNamespace = packet.nsp === this.nsp;
  494. if (!sameNamespace)
  495. return;
  496. switch (packet.type) {
  497. case PacketType.CONNECT:
  498. if (packet.data && packet.data.sid) {
  499. this.onconnect(packet.data.sid, packet.data.pid);
  500. }
  501. else {
  502. this.emitReserved("connect_error", new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));
  503. }
  504. break;
  505. case PacketType.EVENT:
  506. case PacketType.BINARY_EVENT:
  507. this.onevent(packet);
  508. break;
  509. case PacketType.ACK:
  510. case PacketType.BINARY_ACK:
  511. this.onack(packet);
  512. break;
  513. case PacketType.DISCONNECT:
  514. this.ondisconnect();
  515. break;
  516. case PacketType.CONNECT_ERROR:
  517. this.destroy();
  518. const err = new Error(packet.data.message);
  519. // @ts-ignore
  520. err.data = packet.data.data;
  521. this.emitReserved("connect_error", err);
  522. break;
  523. }
  524. }
  525. /**
  526. * Called upon a server event.
  527. *
  528. * @param packet
  529. * @private
  530. */
  531. onevent(packet) {
  532. const args = packet.data || [];
  533. debug("emitting event %j", args);
  534. if (null != packet.id) {
  535. debug("attaching ack callback to event");
  536. args.push(this.ack(packet.id));
  537. }
  538. if (this.connected) {
  539. this.emitEvent(args);
  540. }
  541. else {
  542. this.receiveBuffer.push(Object.freeze(args));
  543. }
  544. }
  545. emitEvent(args) {
  546. if (this._anyListeners && this._anyListeners.length) {
  547. const listeners = this._anyListeners.slice();
  548. for (const listener of listeners) {
  549. listener.apply(this, args);
  550. }
  551. }
  552. super.emit.apply(this, args);
  553. if (this._pid && args.length && typeof args[args.length - 1] === "string") {
  554. this._lastOffset = args[args.length - 1];
  555. }
  556. }
  557. /**
  558. * Produces an ack callback to emit with an event.
  559. *
  560. * @private
  561. */
  562. ack(id) {
  563. const self = this;
  564. let sent = false;
  565. return function (...args) {
  566. // prevent double callbacks
  567. if (sent)
  568. return;
  569. sent = true;
  570. debug("sending ack %j", args);
  571. self.packet({
  572. type: PacketType.ACK,
  573. id: id,
  574. data: args,
  575. });
  576. };
  577. }
  578. /**
  579. * Called upon a server acknowledgement.
  580. *
  581. * @param packet
  582. * @private
  583. */
  584. onack(packet) {
  585. const ack = this.acks[packet.id];
  586. if (typeof ack !== "function") {
  587. debug("bad ack %s", packet.id);
  588. return;
  589. }
  590. delete this.acks[packet.id];
  591. debug("calling ack %s with %j", packet.id, packet.data);
  592. // @ts-ignore FIXME ack is incorrectly inferred as 'never'
  593. if (ack.withError) {
  594. packet.data.unshift(null);
  595. }
  596. // @ts-ignore
  597. ack.apply(this, packet.data);
  598. }
  599. /**
  600. * Called upon server connect.
  601. *
  602. * @private
  603. */
  604. onconnect(id, pid) {
  605. debug("socket connected with id %s", id);
  606. this.id = id;
  607. this.recovered = pid && this._pid === pid;
  608. this._pid = pid; // defined only if connection state recovery is enabled
  609. this.connected = true;
  610. this.emitBuffered();
  611. this._drainQueue(true);
  612. this.emitReserved("connect");
  613. }
  614. /**
  615. * Emit buffered events (received and emitted).
  616. *
  617. * @private
  618. */
  619. emitBuffered() {
  620. this.receiveBuffer.forEach((args) => this.emitEvent(args));
  621. this.receiveBuffer = [];
  622. this.sendBuffer.forEach((packet) => {
  623. this.notifyOutgoingListeners(packet);
  624. this.packet(packet);
  625. });
  626. this.sendBuffer = [];
  627. }
  628. /**
  629. * Called upon server disconnect.
  630. *
  631. * @private
  632. */
  633. ondisconnect() {
  634. debug("server disconnect (%s)", this.nsp);
  635. this.destroy();
  636. this.onclose("io server disconnect");
  637. }
  638. /**
  639. * Called upon forced client/server side disconnections,
  640. * this method ensures the manager stops tracking us and
  641. * that reconnections don't get triggered for this.
  642. *
  643. * @private
  644. */
  645. destroy() {
  646. if (this.subs) {
  647. // clean subscriptions to avoid reconnections
  648. this.subs.forEach((subDestroy) => subDestroy());
  649. this.subs = undefined;
  650. }
  651. this.io["_destroy"](this);
  652. }
  653. /**
  654. * Disconnects the socket manually. In that case, the socket will not try to reconnect.
  655. *
  656. * If this is the last active Socket instance of the {@link Manager}, the low-level connection will be closed.
  657. *
  658. * @example
  659. * const socket = io();
  660. *
  661. * socket.on("disconnect", (reason) => {
  662. * // console.log(reason); prints "io client disconnect"
  663. * });
  664. *
  665. * socket.disconnect();
  666. *
  667. * @return self
  668. */
  669. disconnect() {
  670. if (this.connected) {
  671. debug("performing disconnect (%s)", this.nsp);
  672. this.packet({ type: PacketType.DISCONNECT });
  673. }
  674. // remove socket from pool
  675. this.destroy();
  676. if (this.connected) {
  677. // fire events
  678. this.onclose("io client disconnect");
  679. }
  680. return this;
  681. }
  682. /**
  683. * Alias for {@link disconnect()}.
  684. *
  685. * @return self
  686. */
  687. close() {
  688. return this.disconnect();
  689. }
  690. /**
  691. * Sets the compress flag.
  692. *
  693. * @example
  694. * socket.compress(false).emit("hello");
  695. *
  696. * @param compress - if `true`, compresses the sending data
  697. * @return self
  698. */
  699. compress(compress) {
  700. this.flags.compress = compress;
  701. return this;
  702. }
  703. /**
  704. * Sets a modifier for a subsequent event emission that the event message will be dropped when this socket is not
  705. * ready to send messages.
  706. *
  707. * @example
  708. * socket.volatile.emit("hello"); // the server may or may not receive it
  709. *
  710. * @returns self
  711. */
  712. get volatile() {
  713. this.flags.volatile = true;
  714. return this;
  715. }
  716. /**
  717. * Sets a modifier for a subsequent event emission that the callback will be called with an error when the
  718. * given number of milliseconds have elapsed without an acknowledgement from the server:
  719. *
  720. * @example
  721. * socket.timeout(5000).emit("my-event", (err) => {
  722. * if (err) {
  723. * // the server did not acknowledge the event in the given delay
  724. * }
  725. * });
  726. *
  727. * @returns self
  728. */
  729. timeout(timeout) {
  730. this.flags.timeout = timeout;
  731. return this;
  732. }
  733. /**
  734. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  735. * callback.
  736. *
  737. * @example
  738. * socket.onAny((event, ...args) => {
  739. * console.log(`got ${event}`);
  740. * });
  741. *
  742. * @param listener
  743. */
  744. onAny(listener) {
  745. this._anyListeners = this._anyListeners || [];
  746. this._anyListeners.push(listener);
  747. return this;
  748. }
  749. /**
  750. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  751. * callback. The listener is added to the beginning of the listeners array.
  752. *
  753. * @example
  754. * socket.prependAny((event, ...args) => {
  755. * console.log(`got event ${event}`);
  756. * });
  757. *
  758. * @param listener
  759. */
  760. prependAny(listener) {
  761. this._anyListeners = this._anyListeners || [];
  762. this._anyListeners.unshift(listener);
  763. return this;
  764. }
  765. /**
  766. * Removes the listener that will be fired when any event is emitted.
  767. *
  768. * @example
  769. * const catchAllListener = (event, ...args) => {
  770. * console.log(`got event ${event}`);
  771. * }
  772. *
  773. * socket.onAny(catchAllListener);
  774. *
  775. * // remove a specific listener
  776. * socket.offAny(catchAllListener);
  777. *
  778. * // or remove all listeners
  779. * socket.offAny();
  780. *
  781. * @param listener
  782. */
  783. offAny(listener) {
  784. if (!this._anyListeners) {
  785. return this;
  786. }
  787. if (listener) {
  788. const listeners = this._anyListeners;
  789. for (let i = 0; i < listeners.length; i++) {
  790. if (listener === listeners[i]) {
  791. listeners.splice(i, 1);
  792. return this;
  793. }
  794. }
  795. }
  796. else {
  797. this._anyListeners = [];
  798. }
  799. return this;
  800. }
  801. /**
  802. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  803. * e.g. to remove listeners.
  804. */
  805. listenersAny() {
  806. return this._anyListeners || [];
  807. }
  808. /**
  809. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  810. * callback.
  811. *
  812. * Note: acknowledgements sent to the server are not included.
  813. *
  814. * @example
  815. * socket.onAnyOutgoing((event, ...args) => {
  816. * console.log(`sent event ${event}`);
  817. * });
  818. *
  819. * @param listener
  820. */
  821. onAnyOutgoing(listener) {
  822. this._anyOutgoingListeners = this._anyOutgoingListeners || [];
  823. this._anyOutgoingListeners.push(listener);
  824. return this;
  825. }
  826. /**
  827. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  828. * callback. The listener is added to the beginning of the listeners array.
  829. *
  830. * Note: acknowledgements sent to the server are not included.
  831. *
  832. * @example
  833. * socket.prependAnyOutgoing((event, ...args) => {
  834. * console.log(`sent event ${event}`);
  835. * });
  836. *
  837. * @param listener
  838. */
  839. prependAnyOutgoing(listener) {
  840. this._anyOutgoingListeners = this._anyOutgoingListeners || [];
  841. this._anyOutgoingListeners.unshift(listener);
  842. return this;
  843. }
  844. /**
  845. * Removes the listener that will be fired when any event is emitted.
  846. *
  847. * @example
  848. * const catchAllListener = (event, ...args) => {
  849. * console.log(`sent event ${event}`);
  850. * }
  851. *
  852. * socket.onAnyOutgoing(catchAllListener);
  853. *
  854. * // remove a specific listener
  855. * socket.offAnyOutgoing(catchAllListener);
  856. *
  857. * // or remove all listeners
  858. * socket.offAnyOutgoing();
  859. *
  860. * @param [listener] - the catch-all listener (optional)
  861. */
  862. offAnyOutgoing(listener) {
  863. if (!this._anyOutgoingListeners) {
  864. return this;
  865. }
  866. if (listener) {
  867. const listeners = this._anyOutgoingListeners;
  868. for (let i = 0; i < listeners.length; i++) {
  869. if (listener === listeners[i]) {
  870. listeners.splice(i, 1);
  871. return this;
  872. }
  873. }
  874. }
  875. else {
  876. this._anyOutgoingListeners = [];
  877. }
  878. return this;
  879. }
  880. /**
  881. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  882. * e.g. to remove listeners.
  883. */
  884. listenersAnyOutgoing() {
  885. return this._anyOutgoingListeners || [];
  886. }
  887. /**
  888. * Notify the listeners for each packet sent
  889. *
  890. * @param packet
  891. *
  892. * @private
  893. */
  894. notifyOutgoingListeners(packet) {
  895. if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {
  896. const listeners = this._anyOutgoingListeners.slice();
  897. for (const listener of listeners) {
  898. listener.apply(this, packet.data);
  899. }
  900. }
  901. }
  902. }