webSocketConnection.js 6.1 KB

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