uniapp,h5

socket.js 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904
  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. // the packet has already been acknowledged
  356. return;
  357. }
  358. const hasError = err !== null;
  359. if (hasError) {
  360. if (packet.tryCount > this._opts.retries) {
  361. debug("packet [%d] is discarded after %d tries", packet.id, packet.tryCount);
  362. this._queue.shift();
  363. if (ack) {
  364. ack(err);
  365. }
  366. }
  367. }
  368. else {
  369. debug("packet [%d] was successfully sent", packet.id);
  370. this._queue.shift();
  371. if (ack) {
  372. ack(null, ...responseArgs);
  373. }
  374. }
  375. packet.pending = false;
  376. return this._drainQueue();
  377. });
  378. this._queue.push(packet);
  379. this._drainQueue();
  380. }
  381. /**
  382. * Send the first packet of the queue, and wait for an acknowledgement from the server.
  383. * @param force - whether to resend a packet that has not been acknowledged yet
  384. *
  385. * @private
  386. */
  387. _drainQueue(force = false) {
  388. debug("draining queue");
  389. if (!this.connected || this._queue.length === 0) {
  390. return;
  391. }
  392. const packet = this._queue[0];
  393. if (packet.pending && !force) {
  394. debug("packet [%d] has already been sent and is waiting for an ack", packet.id);
  395. return;
  396. }
  397. packet.pending = true;
  398. packet.tryCount++;
  399. debug("sending packet [%d] (try n°%d)", packet.id, packet.tryCount);
  400. this.flags = packet.flags;
  401. this.emit.apply(this, packet.args);
  402. }
  403. /**
  404. * Sends a packet.
  405. *
  406. * @param packet
  407. * @private
  408. */
  409. packet(packet) {
  410. packet.nsp = this.nsp;
  411. this.io._packet(packet);
  412. }
  413. /**
  414. * Called upon engine `open`.
  415. *
  416. * @private
  417. */
  418. onopen() {
  419. debug("transport is open - connecting");
  420. if (typeof this.auth == "function") {
  421. this.auth((data) => {
  422. this._sendConnectPacket(data);
  423. });
  424. }
  425. else {
  426. this._sendConnectPacket(this.auth);
  427. }
  428. }
  429. /**
  430. * Sends a CONNECT packet to initiate the Socket.IO session.
  431. *
  432. * @param data
  433. * @private
  434. */
  435. _sendConnectPacket(data) {
  436. this.packet({
  437. type: PacketType.CONNECT,
  438. data: this._pid
  439. ? Object.assign({ pid: this._pid, offset: this._lastOffset }, data)
  440. : data,
  441. });
  442. }
  443. /**
  444. * Called upon engine or manager `error`.
  445. *
  446. * @param err
  447. * @private
  448. */
  449. onerror(err) {
  450. if (!this.connected) {
  451. this.emitReserved("connect_error", err);
  452. }
  453. }
  454. /**
  455. * Called upon engine `close`.
  456. *
  457. * @param reason
  458. * @param description
  459. * @private
  460. */
  461. onclose(reason, description) {
  462. debug("close (%s)", reason);
  463. this.connected = false;
  464. delete this.id;
  465. this.emitReserved("disconnect", reason, description);
  466. this._clearAcks();
  467. }
  468. /**
  469. * Clears the acknowledgement handlers upon disconnection, since the client will never receive an acknowledgement from
  470. * the server.
  471. *
  472. * @private
  473. */
  474. _clearAcks() {
  475. Object.keys(this.acks).forEach((id) => {
  476. const isBuffered = this.sendBuffer.some((packet) => String(packet.id) === id);
  477. if (!isBuffered) {
  478. // note: handlers that do not accept an error as first argument are ignored here
  479. const ack = this.acks[id];
  480. delete this.acks[id];
  481. if (ack.withError) {
  482. ack.call(this, new Error("socket has been disconnected"));
  483. }
  484. }
  485. });
  486. }
  487. /**
  488. * Called with socket packet.
  489. *
  490. * @param packet
  491. * @private
  492. */
  493. onpacket(packet) {
  494. const sameNamespace = packet.nsp === this.nsp;
  495. if (!sameNamespace)
  496. return;
  497. switch (packet.type) {
  498. case PacketType.CONNECT:
  499. if (packet.data && packet.data.sid) {
  500. this.onconnect(packet.data.sid, packet.data.pid);
  501. }
  502. else {
  503. 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/)"));
  504. }
  505. break;
  506. case PacketType.EVENT:
  507. case PacketType.BINARY_EVENT:
  508. this.onevent(packet);
  509. break;
  510. case PacketType.ACK:
  511. case PacketType.BINARY_ACK:
  512. this.onack(packet);
  513. break;
  514. case PacketType.DISCONNECT:
  515. this.ondisconnect();
  516. break;
  517. case PacketType.CONNECT_ERROR:
  518. this.destroy();
  519. const err = new Error(packet.data.message);
  520. // @ts-ignore
  521. err.data = packet.data.data;
  522. this.emitReserved("connect_error", err);
  523. break;
  524. }
  525. }
  526. /**
  527. * Called upon a server event.
  528. *
  529. * @param packet
  530. * @private
  531. */
  532. onevent(packet) {
  533. const args = packet.data || [];
  534. debug("emitting event %j", args);
  535. if (null != packet.id) {
  536. debug("attaching ack callback to event");
  537. args.push(this.ack(packet.id));
  538. }
  539. if (this.connected) {
  540. this.emitEvent(args);
  541. }
  542. else {
  543. this.receiveBuffer.push(Object.freeze(args));
  544. }
  545. }
  546. emitEvent(args) {
  547. if (this._anyListeners && this._anyListeners.length) {
  548. const listeners = this._anyListeners.slice();
  549. for (const listener of listeners) {
  550. listener.apply(this, args);
  551. }
  552. }
  553. super.emit.apply(this, args);
  554. if (this._pid && args.length && typeof args[args.length - 1] === "string") {
  555. this._lastOffset = args[args.length - 1];
  556. }
  557. }
  558. /**
  559. * Produces an ack callback to emit with an event.
  560. *
  561. * @private
  562. */
  563. ack(id) {
  564. const self = this;
  565. let sent = false;
  566. return function (...args) {
  567. // prevent double callbacks
  568. if (sent)
  569. return;
  570. sent = true;
  571. debug("sending ack %j", args);
  572. self.packet({
  573. type: PacketType.ACK,
  574. id: id,
  575. data: args,
  576. });
  577. };
  578. }
  579. /**
  580. * Called upon a server acknowledgement.
  581. *
  582. * @param packet
  583. * @private
  584. */
  585. onack(packet) {
  586. const ack = this.acks[packet.id];
  587. if (typeof ack !== "function") {
  588. debug("bad ack %s", packet.id);
  589. return;
  590. }
  591. delete this.acks[packet.id];
  592. debug("calling ack %s with %j", packet.id, packet.data);
  593. // @ts-ignore FIXME ack is incorrectly inferred as 'never'
  594. if (ack.withError) {
  595. packet.data.unshift(null);
  596. }
  597. // @ts-ignore
  598. ack.apply(this, packet.data);
  599. }
  600. /**
  601. * Called upon server connect.
  602. *
  603. * @private
  604. */
  605. onconnect(id, pid) {
  606. debug("socket connected with id %s", id);
  607. this.id = id;
  608. this.recovered = pid && this._pid === pid;
  609. this._pid = pid; // defined only if connection state recovery is enabled
  610. this.connected = true;
  611. this.emitBuffered();
  612. this.emitReserved("connect");
  613. this._drainQueue(true);
  614. }
  615. /**
  616. * Emit buffered events (received and emitted).
  617. *
  618. * @private
  619. */
  620. emitBuffered() {
  621. this.receiveBuffer.forEach((args) => this.emitEvent(args));
  622. this.receiveBuffer = [];
  623. this.sendBuffer.forEach((packet) => {
  624. this.notifyOutgoingListeners(packet);
  625. this.packet(packet);
  626. });
  627. this.sendBuffer = [];
  628. }
  629. /**
  630. * Called upon server disconnect.
  631. *
  632. * @private
  633. */
  634. ondisconnect() {
  635. debug("server disconnect (%s)", this.nsp);
  636. this.destroy();
  637. this.onclose("io server disconnect");
  638. }
  639. /**
  640. * Called upon forced client/server side disconnections,
  641. * this method ensures the manager stops tracking us and
  642. * that reconnections don't get triggered for this.
  643. *
  644. * @private
  645. */
  646. destroy() {
  647. if (this.subs) {
  648. // clean subscriptions to avoid reconnections
  649. this.subs.forEach((subDestroy) => subDestroy());
  650. this.subs = undefined;
  651. }
  652. this.io["_destroy"](this);
  653. }
  654. /**
  655. * Disconnects the socket manually. In that case, the socket will not try to reconnect.
  656. *
  657. * If this is the last active Socket instance of the {@link Manager}, the low-level connection will be closed.
  658. *
  659. * @example
  660. * const socket = io();
  661. *
  662. * socket.on("disconnect", (reason) => {
  663. * // console.log(reason); prints "io client disconnect"
  664. * });
  665. *
  666. * socket.disconnect();
  667. *
  668. * @return self
  669. */
  670. disconnect() {
  671. if (this.connected) {
  672. debug("performing disconnect (%s)", this.nsp);
  673. this.packet({ type: PacketType.DISCONNECT });
  674. }
  675. // remove socket from pool
  676. this.destroy();
  677. if (this.connected) {
  678. // fire events
  679. this.onclose("io client disconnect");
  680. }
  681. return this;
  682. }
  683. /**
  684. * Alias for {@link disconnect()}.
  685. *
  686. * @return self
  687. */
  688. close() {
  689. return this.disconnect();
  690. }
  691. /**
  692. * Sets the compress flag.
  693. *
  694. * @example
  695. * socket.compress(false).emit("hello");
  696. *
  697. * @param compress - if `true`, compresses the sending data
  698. * @return self
  699. */
  700. compress(compress) {
  701. this.flags.compress = compress;
  702. return this;
  703. }
  704. /**
  705. * Sets a modifier for a subsequent event emission that the event message will be dropped when this socket is not
  706. * ready to send messages.
  707. *
  708. * @example
  709. * socket.volatile.emit("hello"); // the server may or may not receive it
  710. *
  711. * @returns self
  712. */
  713. get volatile() {
  714. this.flags.volatile = true;
  715. return this;
  716. }
  717. /**
  718. * Sets a modifier for a subsequent event emission that the callback will be called with an error when the
  719. * given number of milliseconds have elapsed without an acknowledgement from the server:
  720. *
  721. * @example
  722. * socket.timeout(5000).emit("my-event", (err) => {
  723. * if (err) {
  724. * // the server did not acknowledge the event in the given delay
  725. * }
  726. * });
  727. *
  728. * @returns self
  729. */
  730. timeout(timeout) {
  731. this.flags.timeout = timeout;
  732. return this;
  733. }
  734. /**
  735. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  736. * callback.
  737. *
  738. * @example
  739. * socket.onAny((event, ...args) => {
  740. * console.log(`got ${event}`);
  741. * });
  742. *
  743. * @param listener
  744. */
  745. onAny(listener) {
  746. this._anyListeners = this._anyListeners || [];
  747. this._anyListeners.push(listener);
  748. return this;
  749. }
  750. /**
  751. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  752. * callback. The listener is added to the beginning of the listeners array.
  753. *
  754. * @example
  755. * socket.prependAny((event, ...args) => {
  756. * console.log(`got event ${event}`);
  757. * });
  758. *
  759. * @param listener
  760. */
  761. prependAny(listener) {
  762. this._anyListeners = this._anyListeners || [];
  763. this._anyListeners.unshift(listener);
  764. return this;
  765. }
  766. /**
  767. * Removes the listener that will be fired when any event is emitted.
  768. *
  769. * @example
  770. * const catchAllListener = (event, ...args) => {
  771. * console.log(`got event ${event}`);
  772. * }
  773. *
  774. * socket.onAny(catchAllListener);
  775. *
  776. * // remove a specific listener
  777. * socket.offAny(catchAllListener);
  778. *
  779. * // or remove all listeners
  780. * socket.offAny();
  781. *
  782. * @param listener
  783. */
  784. offAny(listener) {
  785. if (!this._anyListeners) {
  786. return this;
  787. }
  788. if (listener) {
  789. const listeners = this._anyListeners;
  790. for (let i = 0; i < listeners.length; i++) {
  791. if (listener === listeners[i]) {
  792. listeners.splice(i, 1);
  793. return this;
  794. }
  795. }
  796. }
  797. else {
  798. this._anyListeners = [];
  799. }
  800. return this;
  801. }
  802. /**
  803. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  804. * e.g. to remove listeners.
  805. */
  806. listenersAny() {
  807. return this._anyListeners || [];
  808. }
  809. /**
  810. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  811. * callback.
  812. *
  813. * Note: acknowledgements sent to the server are not included.
  814. *
  815. * @example
  816. * socket.onAnyOutgoing((event, ...args) => {
  817. * console.log(`sent event ${event}`);
  818. * });
  819. *
  820. * @param listener
  821. */
  822. onAnyOutgoing(listener) {
  823. this._anyOutgoingListeners = this._anyOutgoingListeners || [];
  824. this._anyOutgoingListeners.push(listener);
  825. return this;
  826. }
  827. /**
  828. * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the
  829. * callback. The listener is added to the beginning of the listeners array.
  830. *
  831. * Note: acknowledgements sent to the server are not included.
  832. *
  833. * @example
  834. * socket.prependAnyOutgoing((event, ...args) => {
  835. * console.log(`sent event ${event}`);
  836. * });
  837. *
  838. * @param listener
  839. */
  840. prependAnyOutgoing(listener) {
  841. this._anyOutgoingListeners = this._anyOutgoingListeners || [];
  842. this._anyOutgoingListeners.unshift(listener);
  843. return this;
  844. }
  845. /**
  846. * Removes the listener that will be fired when any event is emitted.
  847. *
  848. * @example
  849. * const catchAllListener = (event, ...args) => {
  850. * console.log(`sent event ${event}`);
  851. * }
  852. *
  853. * socket.onAnyOutgoing(catchAllListener);
  854. *
  855. * // remove a specific listener
  856. * socket.offAnyOutgoing(catchAllListener);
  857. *
  858. * // or remove all listeners
  859. * socket.offAnyOutgoing();
  860. *
  861. * @param [listener] - the catch-all listener (optional)
  862. */
  863. offAnyOutgoing(listener) {
  864. if (!this._anyOutgoingListeners) {
  865. return this;
  866. }
  867. if (listener) {
  868. const listeners = this._anyOutgoingListeners;
  869. for (let i = 0; i < listeners.length; i++) {
  870. if (listener === listeners[i]) {
  871. listeners.splice(i, 1);
  872. return this;
  873. }
  874. }
  875. }
  876. else {
  877. this._anyOutgoingListeners = [];
  878. }
  879. return this;
  880. }
  881. /**
  882. * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,
  883. * e.g. to remove listeners.
  884. */
  885. listenersAnyOutgoing() {
  886. return this._anyOutgoingListeners || [];
  887. }
  888. /**
  889. * Notify the listeners for each packet sent
  890. *
  891. * @param packet
  892. *
  893. * @private
  894. */
  895. notifyOutgoingListeners(packet) {
  896. if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {
  897. const listeners = this._anyOutgoingListeners.slice();
  898. for (const listener of listeners) {
  899. listener.apply(this, packet.data);
  900. }
  901. }
  902. }
  903. }