{"id":383629,"date":"2024-06-29T05:01:25","date_gmt":"2024-06-29T05:01:25","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=383629"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=383629","title":{"rendered":"<span>\u041a\u043e\u0433\u0434\u0430 \u0443\u0436\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0438\u043b\u0438\u2026<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041a\u043e\u0433\u0434\u0430 \u201c\u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043f\u0443\u043b\u043b\u0438\u043d\u0433 \u043a\u0430\u0436\u0434\u044b\u0439 N \u0441\u0435\u043a\u0443\u043d\u0434\u201d \u0441\u0442\u0443\u0447\u0438\u0442\u0441\u044f \u0432\u0430\u043c \u0432 \u043a\u043e\u0434. \u0412\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c \u043f\u0440\u043e \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u044b a.k.a \u043f\u043e\u043b\u043d\u043e\u0434\u0443\u043f\u043b\u0435\u043a\u0441\u043d\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0420\u0435\u0447\u044c \u043f\u043e\u0439\u0434\u0435\u0442 \u043f\u0440\u043e <a href=\"https:\/\/socket.io\/\" rel=\"noopener noreferrer nofollow\"><u>socket.io<\/u><\/a> , \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c web socket \u0430 \u0441\u043a\u043e\u0440\u0435\u0435 \u043c\u0438\u043a\u0441 \u043f\u0440\u0438 \u0443\u0447\u0430\u0441\u0442\u0438\u0438 web socket. \u041d\u043e \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0441\u0440\u0430\u0437\u0443 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438.\u00a0<\/p>\n<p>\u041a\u0435\u0439\u0441 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c:\u00a0<\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435. \u0414\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438 \u043d\u0430\u0436\u0438\u043c\u0430\u044e\u0442 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0438 \u0436\u0434\u0443\u0442 \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442 \u0432\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0430 \u043f\u043e\u0445\u043e\u0434\u0443 \u0435\u0449\u0435 \u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u043f\u0440\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u0440\u043e\u0434\u0435 \u0431\u044b \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c \u043b\u043e\u043d\u0433 \u043f\u0443\u043b\u043b\u0438\u043d\u0433\u043e\u043c, \u043d\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0437\u0430 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0430\u043c\u0438 \u043d\u0430 \u0442\u043e\u0439 \u0436\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435. \u0414\u0430 \u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0433\u043e\u0432\u043e\u0440\u044f \u0441\u0430\u043c\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u0440\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 ws\/wss \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0443\u043b\u043b\u0438\u043d\u0433. \u0422\u0430\u043a \u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 \u0431\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043b\u044e\u0441\u044b, \u0438\u0437 \u043c\u0438\u043d\u0443\u0441\u043e\u0432 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043f\u0430\u043a\u0435\u0442 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.<\/p>\n<p>\u0414\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/github.com\/socketio\/socket.io-client\" rel=\"noopener noreferrer nofollow\"><u>socket-io.client<\/u><\/a>, \u0434\u043b\u044f \u0444\u0440\u043e\u043d\u0442\u0430 \u0432\u0441\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u043d\u0430 React \u0438 \u0441\u0430\u043c\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\/\u0440\u0430\u0437\u043e\u0440\u0432\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0445\u0443\u043a\u0438. \u0414\u043b\u044f \u0442\u0435\u0445 \u043a\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <a href=\"https:\/\/redux-toolkit.js.org\/rtk-query\/usage\/streaming-updates\" rel=\"noopener noreferrer nofollow\"><u>redux-toolkit<\/u><\/a>\u00a0\u0432\u0441\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 RTK.<\/p>\n<p>\u0423 \u0432\u0430\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 <code>createApi({ \u2026 })<\/code> \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0442\u043e\u043c, \u0442\u0430\u043a \u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c.<\/p>\n<pre><code class=\"javascript\">import { createApi, fetchBaseQuery } from '@reduxjs\/toolkit\/query\/react';  export const baseApi = createApi({     reducerPath: 'baseApi',     baseQuery: fetchBaseQuery({ baseUrl: '\/' }),     endpoints: () => ({}), });<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u044c\u044e\u0441\u0435\u0440, \u0435\u0441\u043b\u0438 \u0435\u0433\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u043d\u0435\u0442.<\/p>\n<pre><code class=\"javascript\">const appReducer = combineReducers({ \u2026, [baseApi.reducerPath]: baseApi.reducer, \u2026 });<\/code><\/pre>\n<p>\u0412 middleware \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c =>\u00a0 <code>baseApi.middleware<\/code><\/p>\n<p>\u0418 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u0441\u0432\u043e\u0439 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 baseApi \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0430. <\/p>\n<pre><code class=\"javascript\">import { baseApi } from '..\/api'; import { io } from 'socket.io-client';  export const wsApi = baseApi.injectEndpoints({     endpoints: build => ({         subscribeToEvents: build.query&lt;any, void>({             queryFn: () => ({ data: [] }),             async onCacheEntryAdded(_arg, { dispatch, updateCachedData, cacheEntryRemoved }) {                 \/\/ Path is a prefix that will be used right after domain name                 const socket = io(`${your_url}\/events`, {                     path: '\/socket.io',                 });                  socket.on('disconnect', reason => {                     if (reason === 'io server disconnect') {                         \/\/ the disconnection was initiated by the server, you need to reconnect manually                         socket.connect();                     }                     \/\/ else the socket will automatically try to reconnect                 });                  socket.on(\u2018EVENT_TYPE\u2019, (event: ServerEvent) => {                     \/\/ Here we should add the logic                     updateCachedData(draft => {                         draft.push(event);                     });                 });                  await cacheEntryRemoved;                 socket.close();             },         }),     }),     overrideExisting: false, });  export const { useSubscribeToEventsQuery } = wsApi;<\/code><\/pre>\n<p><code>injectEndpoints<\/code> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0440\u0430\u0437 \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432, \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c <code>overrideExisting: false<\/code> \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u0430 \u043d\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b.<\/p>\n<p><code>useSubscribeToEventsQuery();<\/code> \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u0445\u0443\u043a, \u0432 \u0442\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432 App. \u0415\u0449\u0435 \u0432 api \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>keepUnusedDataFor<\/code> \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0434\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\/\u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e unsubscribe, \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 60 \u0441\u0435\u043a\u0443\u043d\u0434.<\/p>\n<p>\u041d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <a href=\"https:\/\/tsed.io\/tutorials\/socket-io.html#socket-service\" rel=\"noopener noreferrer nofollow\"><u>Ts.Ed<\/u><\/a> (Node js), \u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0445 \u044f\u0437\u044b\u043a\u0430\u0445. \u041d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u043d\u0441\u0442\u0430\u043b\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442\u044b \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 socket.io \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0418 \u0434\u0435\u043b\u0430 \u0437\u0430 \u043c\u0430\u043b\u044b\u043c, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433: <\/p>\n<pre><code class=\"json\">socketIO: { path: '\/socket.io', cors: { origin: '*' \/\/ put your servers } } <\/code><\/pre>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\/\u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0430 \u0442\u0430\u043a \u0436\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"javascript\">import { IO, Nsp, Socket, SocketService, SocketSession } from \"@tsed\/socketio\"; import * as SocketIO from \"socket.io\";  @SocketService(\"\/events\") \/\/ namespace right after path \u2018socket.io\u2019 export class MySocketService {      @Nsp nsp: SocketIO.Namespace | undefined;      \/\/ a map to keep clients by any id you like, a userId or whatever.     public clients: Map&lt;string, SocketIO.Socket> = new Map();       constructor(@IO private io: SocketIO.Server) {     }      \/**      * Triggered when a new client connects to the Namespace.      *\/     $onConnection(@Socket socket: SocketIO.Socket, @SocketSession session: SocketSession) {         this.clients.set(socket.id, socket);     }      \/\/ setup a method to send data to all clients     \/\/ you can use this from any other service or controller.     broadcast(someData: any): void {         this.nsp.emit(\u2018EVENT_TYPE\u2019, { \u2026 }: ServerEvent);     }      \/\/ method to send to a targeted client     sendToSingleClient(idToSendTo: string, someData: any): void {         const socket = this.clients.get(idToSendTo);         if (!socket) return;         socket.emit(\u2018EVENT_TYPE\u2019, { \u2026 }: ServerEvent);     }  }<\/code><\/pre>\n<p>Namespace \u0432 socket.io \u044d\u0442\u043e \u043f\u0443\u0442\u044c <code>\"\/event\"<\/code> \u043f\u043e\u0441\u043b\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 <code>socketIO: { path: 'socket.io' }<\/code>. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0432 \u043b\u044e\u0431\u043e\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u0437\u0430\u0438\u043d\u0436\u0435\u043a\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c. <\/p>\n<p>\u0412\u043e\u043e\u0431\u0449\u0435\u043c \u0442\u043e \u043a\u0430\u043a-\u0442\u043e \u0442\u0430\u043a, \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 side effects. ??<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/594307\/\"> https:\/\/habr.com\/ru\/articles\/594307\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p>\u041a\u043e\u0433\u0434\u0430 \u201c\u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043f\u0443\u043b\u043b\u0438\u043d\u0433 \u043a\u0430\u0436\u0434\u044b\u0439 N \u0441\u0435\u043a\u0443\u043d\u0434\u201d \u0441\u0442\u0443\u0447\u0438\u0442\u0441\u044f \u0432\u0430\u043c \u0432 \u043a\u043e\u0434. \u0412\u0440\u0435\u043c\u044f \u043f\u043e\u0434\u0443\u043c\u0430\u0442\u044c \u043f\u0440\u043e \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u044b a.k.a \u043f\u043e\u043b\u043d\u043e\u0434\u0443\u043f\u043b\u0435\u043a\u0441\u043d\u043e\u0435 \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435.<\/p>\n<p>\u0420\u0435\u0447\u044c \u043f\u043e\u0439\u0434\u0435\u0442 \u043f\u0440\u043e <a href=\"https:\/\/socket.io\/\" rel=\"noopener noreferrer nofollow\"><u>socket.io<\/u><\/a> , \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c web socket \u0430 \u0441\u043a\u043e\u0440\u0435\u0435 \u043c\u0438\u043a\u0441 \u043f\u0440\u0438 \u0443\u0447\u0430\u0441\u0442\u0438\u0438 web socket. \u041d\u043e \u043e\u0447\u0435\u043d\u044c \u0443\u0434\u043e\u0431\u043d\u044b\u0439 \u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u0441\u0440\u0430\u0437\u0443 \u0438\u0437 \u043a\u043e\u0440\u043e\u0431\u043a\u0438.\u00a0<\/p>\n<p>\u041a\u0435\u0439\u0441 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c:\u00a0<\/p>\n<ul>\n<li>\n<p>\u0415\u0441\u0442\u044c \u043c\u043d\u043e\u0433\u043e\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u044e\u0442 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435. \u0414\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438 \u043d\u0430\u0436\u0438\u043c\u0430\u044e\u0442 \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 \u0432 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0438 \u0436\u0434\u0443\u0442 \u043a\u043e\u0433\u0434\u0430 \u0441\u0435\u0440\u0432\u0435\u0440 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0442 \u0432\u0441\u0435 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0430 \u043f\u043e\u0445\u043e\u0434\u0443 \u0435\u0449\u0435 \u0438 \u0440\u0430\u0441\u0441\u043a\u0430\u0436\u0435\u0442 \u043f\u0440\u043e \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435.<\/p>\n<\/li>\n<\/ul>\n<p>\u0412\u0440\u043e\u0434\u0435 \u0431\u044b \u043c\u043e\u0436\u043d\u043e \u043e\u0431\u043e\u0439\u0442\u0438\u0441\u044c \u043b\u043e\u043d\u0433 \u043f\u0443\u043b\u043b\u0438\u043d\u0433\u043e\u043c, \u043d\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0445\u043e\u0447\u0435\u0442\u0441\u044f \u0437\u0430\u0431\u043b\u043e\u043a\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f\u043c \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0437\u0430 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u043c\u043e\u043d\u0438\u0442\u043e\u0440\u0430\u043c\u0438 \u043d\u0430 \u0442\u043e\u0439 \u0436\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435. \u0414\u0430 \u0438 \u0432\u043e\u043e\u0431\u0449\u0435 \u0433\u043e\u0432\u043e\u0440\u044f \u0441\u0430\u043c\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 \u043f\u0440\u0438 \u043e\u0442\u0441\u0443\u0442\u0441\u0442\u0432\u0438\u0438 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 ws\/wss \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0443\u043b\u043b\u0438\u043d\u0433. \u0422\u0430\u043a \u0447\u0442\u043e \u0432\u0440\u043e\u0434\u0435 \u0431\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043b\u044e\u0441\u044b, \u0438\u0437 \u043c\u0438\u043d\u0443\u0441\u043e\u0432 \u0442\u043e\u043b\u044c\u043a\u043e \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043f\u0430\u043a\u0435\u0442 \u043d\u0430 \u043a\u043b\u0438\u0435\u043d\u0442\u0435 \u0438 \u0441\u0435\u0440\u0432\u0435\u0440\u0435.<\/p>\n<p>\u0414\u043b\u044f \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430 <a href=\"https:\/\/github.com\/socketio\/socket.io-client\" rel=\"noopener noreferrer nofollow\"><u>socket-io.client<\/u><\/a>, \u0434\u043b\u044f \u0444\u0440\u043e\u043d\u0442\u0430 \u0432\u0441\u0435 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u043d\u0430 React \u0438 \u0441\u0430\u043c\u043e \u0441\u043e\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u043d\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c\/\u0440\u0430\u0437\u043e\u0440\u0432\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 \u0445\u0443\u043a\u0438. \u0414\u043b\u044f \u0442\u0435\u0445 \u043a\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442 <a href=\"https:\/\/redux-toolkit.js.org\/rtk-query\/usage\/streaming-updates\" rel=\"noopener noreferrer nofollow\"><u>redux-toolkit<\/u><\/a>\u00a0\u0432\u0441\u0435 \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0435\u0440\u0435\u0437 RTK.<\/p>\n<p>\u0423 \u0432\u0430\u0441 \u0434\u043e\u043b\u0436\u0435\u043d \u0431\u044b\u0442\u044c \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 <code>createApi({ \u2026 })<\/code> \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u0432\u0441\u0435 \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u044b \u0438 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0442\u043e\u043c, \u0442\u0430\u043a \u0436\u0435 \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c.<\/p>\n<pre><code class=\"javascript\">import { createApi, fetchBaseQuery } from '@reduxjs\/toolkit\/query\/react';  export const baseApi = createApi({     reducerPath: 'baseApi',     baseQuery: fetchBaseQuery({ baseUrl: '\/' }),     endpoints: () => ({}), });<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0432 \u043a\u043e\u0440\u043d\u0435\u0432\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u044f \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043d\u043e\u0432\u044b\u0439 \u0440\u0435\u0434\u044c\u044e\u0441\u0435\u0440, \u0435\u0441\u043b\u0438 \u0435\u0433\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u043d\u0435\u0442.<\/p>\n<pre><code class=\"javascript\">const appReducer = combineReducers({ \u2026, [baseApi.reducerPath]: baseApi.reducer, \u2026 });<\/code><\/pre>\n<p>\u0412 middleware \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c =>\u00a0 <code>baseApi.middleware<\/code><\/p>\n<p>\u0418 \u0440\u0430\u0441\u0448\u0438\u0440\u044f\u0435\u043c \u0441\u0432\u043e\u0439 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 baseApi \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b\u043e\u043c \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0432\u0435\u0431\u0441\u043e\u043a\u0435\u0442\u0430. <\/p>\n<pre><code class=\"javascript\">import { baseApi } from '..\/api'; import { io } from 'socket.io-client';  export const wsApi = baseApi.injectEndpoints({     endpoints: build => ({         subscribeToEvents: build.query&lt;any, void>({             queryFn: () => ({ data: [] }),             async onCacheEntryAdded(_arg, { dispatch, updateCachedData, cacheEntryRemoved }) {                 \/\/ Path is a prefix that will be used right after domain name                 const socket = io(`${your_url}\/events`, {                     path: '\/socket.io',                 });                  socket.on('disconnect', reason => {                     if (reason === 'io server disconnect') {                         \/\/ the disconnection was initiated by the server, you need to reconnect manually                         socket.connect();                     }                     \/\/ else the socket will automatically try to reconnect                 });                  socket.on(\u2018EVENT_TYPE\u2019, (event: ServerEvent) => {                     \/\/ Here we should add the logic                     updateCachedData(draft => {                         draft.push(event);                     });                 });                  await cacheEntryRemoved;                 socket.close();             },         }),     }),     overrideExisting: false, });  export const { useSubscribeToEventsQuery } = wsApi;<\/code><\/pre>\n<p><code>injectEndpoints<\/code> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0440\u0430\u0437 \u0434\u043b\u044f \u0440\u0430\u0441\u0448\u0438\u0440\u0435\u043d\u0438\u044f \u044d\u043d\u0434\u043f\u043e\u0438\u043d\u0442\u043e\u0432, \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c <code>overrideExisting: false<\/code> \u0447\u0442\u043e\u0431\u044b \u0440\u0430\u0441\u0448\u0438\u0440\u0438\u0442\u044c \u0430 \u043d\u0435 \u043f\u0435\u0440\u0435\u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0442\u044c \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b.<\/p>\n<p><code>useSubscribeToEventsQuery();<\/code> \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u044b\u0439 \u0445\u0443\u043a, \u0432 \u0442\u043e\u043c \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0435 \u0432 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0436\u0435\u043b\u0430\u0435\u0442\u0435 \u043f\u043e\u0434\u043f\u0438\u0441\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u0441\u043e\u0431\u044b\u0442\u0438\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 \u0432 App. \u0415\u0449\u0435 \u0432 api \u0435\u0441\u0442\u044c \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e <code>keepUnusedDataFor<\/code> \u0434\u043b\u044f \u0442\u043e\u0433\u043e \u0447\u0442\u043e\u0431\u044b \u0437\u0430\u0434\u0430\u0442\u044c \u0432\u0440\u0435\u043c\u044f \u0432 \u0441\u0435\u043a\u0443\u043d\u0434\u0430\u0445 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u043d\u0438\u044f \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u044f\/\u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e\u0441\u043b\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0435\u0433\u043e unsubscribe, \u043f\u043e \u0443\u043c\u043e\u043b\u0447\u0430\u043d\u0438\u044e 60 \u0441\u0435\u043a\u0443\u043d\u0434.<\/p>\n<p>\u041d\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <a href=\"https:\/\/tsed.io\/tutorials\/socket-io.html#socket-service\" rel=\"noopener noreferrer nofollow\"><u>Ts.Ed<\/u><\/a> (Node js), \u043d\u043e \u0442\u0430\u043a\u0436\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u0443\u044e\u0442 \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u043d\u0430 \u0434\u0440\u0443\u0433\u0438\u0445 \u044f\u0437\u044b\u043a\u0430\u0445. \u041d\u0443\u0436\u043d\u043e \u043f\u0440\u043e\u0438\u043d\u0441\u0442\u0430\u043b\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u0430\u043a\u0435\u0442\u044b \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u044b\u0435 \u0441 socket.io \u0434\u043b\u044f \u0441\u0435\u0440\u0432\u0435\u0440\u0430. \u0418 \u0434\u0435\u043b\u0430 \u0437\u0430 \u043c\u0430\u043b\u044b\u043c, \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0444\u0438\u0433: <\/p>\n<pre><code class=\"json\">socketIO: { path: '\/socket.io', cors: { origin: '*' \/\/ put your servers } } <\/code><\/pre>\n<p>\u0418 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442 \u043f\u043e\u0434\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435\/\u043e\u0442\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430 \u0430 \u0442\u0430\u043a \u0436\u0435 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0435\u0442 \u0438 \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f.<\/p>\n<pre><code class=\"javascript\">import { IO, Nsp, Socket, SocketService, SocketSession } from \"@tsed\/socketio\"; import * as SocketIO from \"socket.io\";  @SocketService(\"\/events\") \/\/ namespace right after path \u2018socket.io\u2019 export class MySocketService {      @Nsp nsp: SocketIO.Namespace | undefined;      \/\/ a map to keep clients by any id you like, a userId or whatever.     public clients: Map&lt;string, SocketIO.Socket> = new Map();       constructor(@IO private io: SocketIO.Server) {     }      \/**      * Triggered when a new client connects to the Namespace.      *\/     $onConnection(@Socket socket: SocketIO.Socket, @SocketSession session: SocketSession) {         this.clients.set(socket.id, socket);     }      \/\/ setup a method to send data to all clients     \/\/ you can use this from any other service or controller.     broadcast(someData: any): void {         this.nsp.emit(\u2018EVENT_TYPE\u2019, { \u2026 }: ServerEvent);     }      \/\/ method to send to a targeted client     sendToSingleClient(idToSendTo: string, someData: any): void {         const socket = this.clients.get(idToSendTo);         if (!socket) return;         socket.emit(\u2018EVENT_TYPE\u2019, { \u2026 }: ServerEvent);     }  }<\/code><\/pre>\n<p>Namespace \u0432 socket.io \u044d\u0442\u043e \u043f\u0443\u0442\u044c <code>\"\/event\"<\/code> \u043f\u043e\u0441\u043b\u0435 \u043e\u0441\u043d\u043e\u0432\u043d\u043e\u0433\u043e \u043f\u0443\u0442\u0438 \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 <code>socketIO: { path: 'socket.io' }<\/code>. \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0432 \u043b\u044e\u0431\u043e\u043c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u0437\u0430\u0438\u043d\u0436\u0435\u043a\u0442\u0438\u0442\u044c \u0441\u0435\u0440\u0432\u0438\u0441 \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u043a\u043b\u0438\u0435\u043d\u0442\u0430\u043c. <\/p>\n<p>\u0412\u043e\u043e\u0431\u0449\u0435\u043c \u0442\u043e \u043a\u0430\u043a-\u0442\u043e \u0442\u0430\u043a, \u043f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043a\u043b\u0438\u0435\u043d\u0442 \u043c\u043e\u0436\u0435\u0442 \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u044f \u0438 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u044b\u0435 side effects. ??<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/594307\/\"> https:\/\/habr.com\/ru\/articles\/594307\/<\/a><br \/><\/br><\/br><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-383629","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/383629","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=383629"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/383629\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=383629"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=383629"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=383629"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}