WebSocketConnection.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. const cleanPeriod = 60*1000;//1 минута
  2. class WebSocketConnection {
  3. //messageLifeTime в минутах (cleanPeriod)
  4. constructor(messageLifeTime = 5) {
  5. this.ws = null;
  6. this.timer = null;
  7. this.listeners = [];
  8. this.messageQueue = [];
  9. this.messageLifeTime = messageLifeTime;
  10. this.requestId = 0;
  11. }
  12. addListener(listener) {
  13. if (this.listeners.indexOf(listener) < 0)
  14. this.listeners.push(Object.assign({regTime: Date.now()}, listener));
  15. }
  16. //рассылаем сообщение и удаляем те обработчики, которые его получили
  17. emit(mes, isError) {
  18. const len = this.listeners.length;
  19. if (len > 0) {
  20. let newListeners = [];
  21. for (const listener of this.listeners) {
  22. let emitted = false;
  23. if (isError) {
  24. if (listener.onError)
  25. listener.onError(mes);
  26. emitted = true;
  27. } else {
  28. if (listener.onMessage) {
  29. if (listener.requestId) {
  30. if (listener.requestId === mes.requestId) {
  31. listener.onMessage(mes);
  32. emitted = true;
  33. }
  34. } else {
  35. listener.onMessage(mes);
  36. emitted = true;
  37. }
  38. } else {
  39. emitted = true;
  40. }
  41. }
  42. if (!emitted)
  43. newListeners.push(listener);
  44. }
  45. this.listeners = newListeners;
  46. }
  47. return this.listeners.length != len;
  48. }
  49. open() {
  50. return new Promise((resolve, reject) => {
  51. if (this.ws && this.ws.readyState == WebSocket.OPEN) {
  52. resolve(this.ws);
  53. } else {
  54. this.ws = new WebSocket(`ws://${window.location.host}`);
  55. if (this.timer) {
  56. clearTimeout(this.timer);
  57. }
  58. this.timer = setTimeout(() => { this.periodicClean(); }, cleanPeriod);
  59. let resolved = false;
  60. this.ws.onopen = (e) => {
  61. resolved = true;
  62. resolve(e);
  63. };
  64. this.ws.onmessage = (e) => {
  65. try {
  66. const mes = JSON.parse(e.data);
  67. this.messageQueue.push({regTime: Date.now(), mes});
  68. let newMessageQueue = [];
  69. for (const message of this.messageQueue) {
  70. if (!this.emit(message.mes)) {
  71. newMessageQueue.push(message);
  72. }
  73. }
  74. this.messageQueue = newMessageQueue;
  75. } catch (e) {
  76. this.emit(e.message, true);
  77. }
  78. };
  79. this.ws.onerror = (e) => {
  80. this.emit(e.message, true);
  81. if (!resolved)
  82. reject(e);
  83. };
  84. }
  85. });
  86. }
  87. //timeout в минутах (cleanPeriod)
  88. message(timeout = 2) {
  89. return new Promise((resolve, reject) => {
  90. this.addListener({
  91. timeout,
  92. onMessage: (mes) => {
  93. resolve(mes);
  94. },
  95. onError: (e) => {
  96. reject(e);
  97. }
  98. });
  99. });
  100. }
  101. //timeout в минутах (cleanPeriod)
  102. messageId(requestId, timeout = 2) {
  103. return new Promise((resolve, reject) => {
  104. this.addListener({
  105. requestId,
  106. timeout,
  107. onMessage: (mes) => {
  108. resolve(mes);
  109. },
  110. onError: (e) => {
  111. reject(e);
  112. }
  113. });
  114. });
  115. }
  116. send(req) {
  117. if (this.ws && this.ws.readyState == WebSocket.OPEN) {
  118. this.ws.send(JSON.stringify(req));
  119. } else {
  120. throw new Error('WebSocket connection is not ready');
  121. }
  122. }
  123. sendId(req) {
  124. if (this.ws && this.ws.readyState == WebSocket.OPEN) {
  125. const requestId = ++this.requestId;
  126. this.ws.send(Object.assign({requestId}, JSON.stringify(req)));
  127. return requestId;
  128. } else {
  129. throw new Error('WebSocket connection is not ready');
  130. }
  131. }
  132. close() {
  133. if (this.ws && this.ws.readyState == WebSocket.OPEN) {
  134. this.ws.close();
  135. }
  136. }
  137. periodicClean() {
  138. try {
  139. this.timer = null;
  140. const now = Date.now();
  141. //чистка listeners
  142. let newListeners = [];
  143. for (const listener of this.listeners) {
  144. if (now - listener.regTime < listener.timeout*cleanPeriod - 50) {
  145. newListeners.push(listener);
  146. }
  147. }
  148. this.listeners = newListeners;
  149. //чистка messageQueue
  150. let newMessageQueue = [];
  151. for (const message of this.messageQueue) {
  152. if (now - message.regTime < this.messageLifeTime*cleanPeriod - 50) {
  153. newMessageQueue.push(message);
  154. }
  155. }
  156. this.messageQueue = newMessageQueue;
  157. } finally {
  158. if (this.ws.readyState == WebSocket.OPEN) {
  159. this.timer = setTimeout(() => { this.periodicClean(); }, cleanPeriod);
  160. }
  161. }
  162. }
  163. }
  164. export default WebSocketConnection;