{"id":326553,"date":"2021-07-16T15:00:50","date_gmt":"2021-07-16T15:00:50","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=326553"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=326553","title":{"rendered":"\u0418\u0437\u0443\u0447\u0430\u0439 observable, \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u044f observable"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u2014 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 Ben Lesh&nbsp;<a href=\"https:\/\/benlesh.com\/posts\/learning-observable-by-building-observable\/\" rel=\"noopener noreferrer nofollow\"><u>\u201cLearning Observable By Building Observable\u201d<\/u><\/a>. \u0422\u0430\u043a\u0436\u0435 \u044f \u0432\u0435\u0434\u0443 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u043a\u0430\u043d\u0430\u043b \u201c<a href=\"https:\/\/t.me\/frontend_pasta\" rel=\"noopener noreferrer nofollow\"><u>Frontend \u043f\u043e-\u0444\u043b\u043e\u0442\u0441\u043a\u0438<\/u><\/a>\u201d, \u0433\u0434\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u043f\u0440\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u0432\u0435\u0449\u0438 \u0438\u0437 \u043c\u0438\u0440\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u200b<\/p>\n<h2>\u0412\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u042d\u0442\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0440\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0439 \u043c\u043d\u043e\u044e \u0432 2016 \u0433\u043e\u0434\u0443, \u0438 \u0434\u043e\u043a\u043b\u0430\u0434\u0430, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u044f \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u043b \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437. \u042f \u0445\u043e\u0447\u0443 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0435\u0440\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0438, \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0435\u0433\u043e. \u0426\u0435\u043b\u044c \u2014 \u043f\u043e\u043c\u043e\u0447\u044c \u043b\u044e\u0434\u044f\u043c \u043f\u043e\u043d\u044f\u0442\u044c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 observable. \u041d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e observable \u0438\u0437 RxJS, \u043d\u043e \u0438 \u043b\u044e\u0431\u043e\u0439 observable (\u0434\u0430, \u0438\u0445 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e) \u043a\u0430\u043a \u0442\u0438\u043f.<\/p>\n<h3>Observables \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438<\/h3>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c \u044d\u0442\u043e, \u044f \u0445\u043e\u0447\u0443 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0440\u044f\u0434\u043e\u043c \u0434\u0432\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0430. \u041e\u0434\u0438\u043d \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f observable \u0438\u0437 RxJS, \u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439. \u041e\u0431\u0430 \u044d\u0442\u0438\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0438\u043c\u0435\u044e\u0442 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p><strong>RxJS Observable<\/strong><\/p>\n<p>\u042d\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e observable, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e RxJS, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0434\u0430\u0435\u0442 \u0442\u0440\u0438 \u0447\u0438\u0441\u043b\u0430 \u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044f. \u0415\u0441\u043b\u0438 \u0432\u044b \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 RxJS, \u044d\u0442\u043e \u044d\u043a\u0432\u0438\u0432\u0430\u043b\u0435\u043d\u0442 (1, 2, 3).<\/p>\n<pre><code class=\"javascript\">import { Observable } from 'rxjs';   const source = new Observable&lt;number&gt;((subscriber) =&gt; {   subscriber.next(1);   subscriber.next(2);   subscriber.next(3);   subscriber.complete(); });   \/\/ Usage console.log('start'); source.subscribe({   next: console.log,   complete: () =&gt; console.log('done'), }); console.log('stop');<\/code><\/pre>\n<p><strong>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u201cobservable\u201d<\/strong><\/p>\n<pre><code class=\"javascript\">\/**  * A simple object with a `next` and `complete` callback on it.  *\/ interface Observer&lt;T&gt; {   next: (value: T) =&gt; void;   complete: () =&gt; void; }   \/**  * A function that takes a simple object with callbacks  * and does something them.  *\/ const source = (subscriber: Observer&lt;number&gt;) =&gt; {   subscriber.next(1);   subscriber.next(2);   subscriber.next(3);   subscriber.complete(); };   \/\/ Usage console.log('start'); source({   next: console.log,   complete: () =&gt; console.log('done'), }); console.log('stop');<\/code><\/pre>\n<p><strong>\u0412\u044b\u0432\u043e\u0434 (\u043e\u0431\u043e\u0438\u0445!)<\/strong><\/p>\n<pre><code>\"start\" 1 2 3 \"done\" \"stop\"<\/code><\/pre>\n<p>\u042f \u0445\u043e\u0447\u0443, \u0447\u0442\u043e\u0431\u044b \u0432\u044b \u043e\u0431\u0440\u0430\u0442\u0438\u043b\u0438 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0441\u0445\u043e\u0434\u0441\u0442\u0432\u043e. \u0412 \u043e\u0431\u043e\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 next \u0438 complete. \u0412 \u043e\u0431\u043e\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0435 next \u0438 complete \u0432 \u0442\u0435\u043b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0412 \u043e\u0431\u043e\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u0432\u044b \u043d\u0435 \u0432\u044b\u0437\u043e\u0432\u0435\u0442\u0435 source.subscribe() \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0437\u043e\u0432\u0435\u0442\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u043a\u0430\u043a source() \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435. \u042d\u0442\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e observables \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<h3>\u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438? \u0427\u0442\u043e \u00ab\u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e\u00bb \u0432 observable?<\/h3>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e \u043f\u043e\u0434\u043c\u0435\u0447\u0435\u043d\u043e. \u0415\u0441\u043b\u0438 \u0431\u044b \u0432\u044b \u0431\u044b\u043b\u0438 \u043e\u0447\u0435\u043d\u044c \u043e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u044b, \u0432\u044b, \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u0435\u0432. \u041d\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438 \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u00ab\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0430\u00bb. \u041d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430, \u0435\u0441\u043b\u0438 \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 observable \u0442\u0430\u043a\u043e\u0432\u0430, \u0447\u0442\u043e next \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e.<\/p>\n<pre><code class=\"javascript\">const source = function (subscriber: Observer&lt;number&gt;) {   subscriber.next(1);   subscriber.next(2);   subscriber.next(3);   subscriber.complete();   subscriber.next(4); \/\/ \u0423\u043f\u0441, \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c };<\/code><\/pre>\n<p>\u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432\u044b\u0448\u0435 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u00abdone\u00bb, \u0430 \u0437\u0430\u0442\u0435\u043c \u0441\u0440\u0430\u0437\u0443 4. \u0422\u0430\u043a\u043e\u0433\u043e \u0431\u044b\u0442\u044c \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e! \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043c\u0435\u0442\u043e\u0434 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f. \u042d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0437\u0430\u043a\u043b\u044e\u0447\u0438\u0432 \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u043a\u043b\u0430\u0441\u0441.<\/p>\n<pre><code class=\"javascript\">\/**  * A class used to wrap a user-provided Observer. Since the  * observer is just a plain objects with a couple of callbacks on it,  * this type will wrap that to ensure `next` does nothing if called after  * `complete` has been called, and that nothing happens if `complete`  * is called more than once.  *\/ class SafeSubscriber&lt;T&gt; {   closed = false;     constructor(private destination: Observer&lt;T&gt;) {}     next(value: T) {     \/\/ Check to see if this is \"closed\" before nexting.     if (!this.closed) {       this.destination.next(value);     }   }     complete() {     \/\/ Make sure we're not completing an already \"closed\" subscriber.     if (!this.closed) {       \/\/ We're closed now.       this.closed = true;       this.destination.complete();     }   } }   \/**  * A class to wrap our function, to ensure that when the function is  * called with an observer, that observer is wrapped with a SafeSubscriber  *\/ class Observable&lt;T&gt; {   constructor(private _wrappedFunc: (subscriber: Observer&lt;T&gt;) =&gt; void) {}     subscribe(observer: Observer&lt;T&gt;): void {     \/\/ We can wrap our observer in a \"safe subscriber\" that     \/\/ does the work of making sure it's not closed.     const subscriber = new SafeSubscriber(observer);     this._wrappedFunc(subscriber);   } }   \/\/ Usage \/\/ Now 4 won't be nexted after we complete. const source = new Observable((subscriber) =&gt; {   subscriber.next(1);   subscriber.next(2);   subscriber.next(3);   subscriber.complete();   subscriber.next(4); \/\/ this does nothing. });<\/code><\/pre>\n<h3>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 Partial Observers<\/h3>\n<p>\u0414\u0440\u0443\u0433\u043e\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u2014 \u00abpartial\u00bb observer. \u0414\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438, observable, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u0442\u043e\u0434 next \u0438\u043b\u0438 complete (\u0438\u043b\u0438, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043c\u0435\u0442\u043e\u0434 \u043e\u0448\u0438\u0431\u043a\u0438, \u043d\u043e \u043c\u044b \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0437\u0436\u0435). \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043b\u0435\u0433\u043a\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0441 \u044d\u0442\u0438\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u043c \u0438\u043c\u0435\u044f \u043d\u0430\u0448 \u0442\u0438\u043f observable, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u0432 \u043d\u0430\u0448\u0435\u043c SafeSubscriber:<\/p>\n<pre><code class=\"javascript\">class SafeSubscriber&lt;T&gt; {   closed = false;     constructor(private destination: Partial&lt;Observer&lt;T&gt;&gt;) {}     next(value: T) {     if (!this.closed) {       this.destination.next?.(value); \/\/ Note the ?. check here.     }   }     complete() {     if (!this.closed) {       this.closed = true;       this.destination.complete?.(); \/\/ And here.     }   } }<\/code><\/pre>\n<h2>\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435<\/h2>\n<p>\u0423\u0432\u0435\u0434\u043e\u043c\u0438\u0442\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u0442\u0430\u043a \u0436\u0435 \u043f\u0440\u043e\u0441\u0442\u043e, \u043a\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a \u043d\u0430\u0448\u0438\u043c Observer \u0438 SafeSubscriber. \u0421\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0430 \u043d\u0430 complete \u0432\u044b\u0448\u0435. \u0418 \u043e\u0448\u0438\u0431\u043a\u0430, \u0438 complete \u0441\u0447\u0438\u0442\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435\u043c \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f.<\/p>\n<p>Observer \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0448\u0438\u0431\u043e\u043a:<\/p>\n<pre><code>interface Observer&lt;T&gt; {   next: (value: T) =&gt; void;   complete: () =&gt; void;   error: (err: any) =&gt; void; }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043d\u0430\u0448 SafeSubscriber error \u043c\u0435\u0442\u043e\u0434:<\/p>\n<pre><code class=\"javascript\">class SafeSubscriber&lt;T&gt; {   closed = false;     constructor(private destination: Partial&lt;Observer&lt;T&gt;&gt;) {}     next(value: T) {     if (!this.closed) {       this.destination.next?.(value);     }   }     complete() {     if (!this.closed) {       this.closed = true;       this.destination.complete?.();     }   }     error(err: any) {     if (!this.closed) {       this.closed = true;       this.destination.error?.(err);     }   } }<\/code><\/pre>\n<h3>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043a\u0435\u0439\u0441 \u0434\u043b\u044f \u043e\u0442\u043c\u0435\u043d\u044b \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438<\/h3>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f observable \u2014 \u043e\u0431\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 WebSocket.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448 \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u044b\u0439 observable \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"javascript\">const helloSocket = new Observable&lt;string&gt;((subscriber) =&gt; {   \/\/ Open a socket.   const socket = new WebSocket('wss:\/\/echo.websocket.org');     socket.onopen = () =&gt; {     \/\/ Once it's open, send some text.     socket.send('Hello, World!');   };     socket.onmessage = (e) =&gt; {     \/\/ When it echoes the text back (in the case of this particular server)     \/\/ notify the consumer.     subscriber.next(e.data);   };     socket.onclose = (e) =&gt; {     \/\/ Oh! we closed!     if (e.wasClean) {       \/\/ ...because the server said it was done.       subscriber.complete();     } else {       \/\/ ...because of something bad. Maybe we lost network or something.       subscriber.error(new Error('Socket closed dirty!'));     }   }; });   \/\/ Start the websocket and log the echoes helloSocket.subscribe({   next: console.log,   complete: () =&gt; console.log('server closed'),   error: console.error, });<\/code><\/pre>\n<p>\u041d\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043d\u0430\u0448 observable, \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0441\u043e\u043a\u0435\u0442. \u041d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443. \u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0443\u044e \u043d\u0430\u0448\u0443 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438.<\/p>\n<pre><code class=\"javascript\">const source = (subscriber: Observer&lt;string&gt;) =&gt; {   const socket = new WebSocket('wss:\/\/echo.websocket.org');     socket.onopen = () =&gt; {     socket.send('Hello, World!');   };     socket.onmessage = (e) =&gt; {     subscriber.next(e.data);   };     socket.onclose = (e) =&gt; {     if (e.wasClean) {       subscriber.complete();     } else {       subscriber.error(new Error('Socket closed dirty!'));     }   };     return () =&gt; {     if (socket.readyState &lt;= WebSocket.OPEN) {       socket.close();     }   }; };   const teardown = source({   next: console.log,   complete: () =&gt; console.log('done'),   error: console.error, });   \/\/ Decide you really don't want to keep the socket open. teardown();<\/code><\/pre>\n<p>\u0415\u0449\u0435 \u043e\u0434\u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u0430\u044f \u0441 WebSocket, \u043d\u043e \u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u043c\u0438, \u2014 \u044d\u0442\u043e \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438, \u043a\u043e\u0433\u0434\u0430 \u0430\u0432\u0442\u043e\u0440 observable \u0440\u0435\u0448\u0430\u0435\u0442, \u0447\u0442\u043e \u043e\u043d \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 \u0438\u043b\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f, \u0438 \u043e\u043d \u0445\u043e\u0447\u0435\u0442 \u0443\u0432\u0435\u0434\u043e\u043c\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0435\u0433\u043e. \u0411\u044b\u043b\u043e \u0431\u044b \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0438\u043c\u0435\u0442\u044c \u0435\u0434\u0438\u043d\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 subscriber.error \u0438\u043b\u0438 subscriber.complete \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u043e\u0441\u044c \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435.<\/p>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 SafeSubscriber \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u0430 Subscription.<\/p>\n<pre><code class=\"javascript\">\/**  * Our subscription type. This is to manage teardowns.  *\/ class Subscription {   private teardowns = new Set&lt;() =&gt; void&gt;();     add(teardown: () =&gt; void) {     this.teardowns.add(teardown);   }     unsubscribe() {     for (const teardown of this.teardowns) {       teardown();     }     this.teardowns.clear();   } }   class SafeSubscriber&lt;T&gt; {   closed = false;     constructor(     private destination: Partial&lt;Observer&lt;T&gt;&gt;,     private subscription: Subscription,   ) {     \/\/ Make sure that if the subscription is unsubscribed,     \/\/ we don't let any more notifications through this subscriber.     subscription.add(() =&gt; (this.closed = true));   }     next(value: T) {     if (!this.closed) {       this.destination.next?.(value);     }   }     complete() {     if (!this.closed) {       this.closed = true;       this.destination.complete?.();       this.subscription.unsubscribe();     }   }     error(err: any) {     if (!this.closed) {       this.closed = true;       this.destination.error?.(err);       this.subscription.unsubscribe();     }   } }   class Observable&lt;T&gt; {   constructor(private _wrappedFunc: (subscriber: Observer&lt;T&gt;) =&gt; () =&gt; void) {}     subscribe(observer: Observer&lt;T&gt;) {     const subscription = new Subscription();     const subscriber = new SafeSubscriber(observer, subscription);     subscription.add(this._wrappedFunc(subscriber));     return subscription;   } }   const helloSocket = new Observable&lt;string&gt;((subscriber) =&gt; {   const socket = new WebSocket('wss:\/\/echo.websocket.org');     socket.onopen = () =&gt; {     socket.send('Hello, World!');   };     socket.onmessage = (e) =&gt; {     subscriber.next(e.data);   };     socket.onclose = (e) =&gt; {     if (e.wasClean) {       subscriber.complete();     } else {       subscriber.error(new Error('Socket closed dirty!'));     }   };     return () =&gt; {     if (socket.readyState &lt;= WebSocket.OPEN) {       socket.close();     }   }; });   const subscription = helloSocket.subscribe({   next: console.log,   complete: () =&gt; console.log('server closed'),   error: console.error, });   \/\/ Later, we can unsubscribe! subscription.unsubscribe();<\/code><\/pre>\n<h3>\u042d\u0442\u043e \u0432\u0441\u0451! Observables \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438!<\/h3>\n<p>\u042f \u0437\u043d\u0430\u044e, \u0447\u0442\u043e \u044d\u0442\u043e \u0431\u044b\u043b\u043e \u043e\u0447\u0435\u043d\u044c \u041c\u041d\u041e\u0413\u041e \u043a\u043e\u0434\u0430, \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e\u043c\u043e\u0433\u043b\u043e \u0432\u0430\u043c \u043f\u043e\u043d\u044f\u0442\u044c, \u0447\u0442\u043e \u0432\u0445\u043e\u0434\u0438\u0442 \u0432 \u0442\u0438\u043f observable (\u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0442\u0438\u043f \u0438\u0437&nbsp;<a href=\"https:\/\/rxjs.dev\/\" rel=\"noopener noreferrer nofollow\"><u>rxjs<\/u><\/a>), \u0438 \u043f\u043e\u043c\u043e\u0433\u043b\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u0440\u043e\u044f\u0441\u043d\u0438\u0442\u044c \u0435\u0433\u043e. \u042d\u0442\u043e \u0443\u0436 \u0442\u043e\u0447\u043d\u043e \u043d\u0435 \u0432\u043e\u043b\u0448\u0435\u0431\u0441\u0442\u0432\u043e.<\/p>\n<p>\u0422\u043e\u0433\u0434\u0430 \u044f \u0431\u044b \u043f\u043e\u0441\u043e\u0432\u0435\u0442\u043e\u0432\u0430\u043b \u0432\u0430\u043c \u0441\u043f\u0440\u043e\u0441\u0438\u0442\u044c \u0441\u0435\u0431\u044f: \u00ab\u0414\u043b\u044f \u0447\u0435\u0433\u043e \u043d\u0443\u0436\u043d\u044b observables?\u00bb \u0438, \u0447\u0435\u0441\u0442\u043d\u043e \u0433\u043e\u0432\u043e\u0440\u044f, \u0441\u0430\u043c\u044b\u0439 \u043a\u043e\u0440\u043e\u0442\u043a\u0438\u0439 \u043e\u0442\u0432\u0435\u0442: \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c observables \u0434\u043b\u044f \u0432\u0441\u0435\u0433\u043e, \u0434\u043b\u044f \u0447\u0435\u0433\u043e \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e. \u0421\u0430\u043c\u0430\u044f \u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0440\u0430\u0437\u043d\u0438\u0446\u0430 \u2014 \u044d\u0442\u043e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u043d\u0430\u0431\u043e\u0440 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0439 \u0432 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0438 \u043e\u0431\u0440\u0430\u0442\u043d\u044b\u0445 \u0432\u044b\u0437\u043e\u0432\u043e\u0432 \u0438 \u0442\u043e, \u0447\u0442\u043e, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0432\u0430\u0436\u043d\u043e, \u0432 \u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0438 \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b.<\/p>\n<p>\u0412\u0430\u0436\u043d\u043e \u043e\u0442\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043e\u043f\u0438\u0441\u0430\u043d\u043d\u0430\u044f \u0432\u044b\u0448\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u041d\u0415 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0435\u043c-\u0442\u043e, \u0447\u0442\u043e \u0432\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u043e\u0441\u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0438\u043b\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0432 \u043f\u0440\u043e\u0434\u0430\u043a\u0448\u0435\u043d\u0435. \u042f \u0431\u044b \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u043e\u0432\u0430\u043b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c observable \u0438\u0437 RxJS, \u043f\u043e\u0441\u043a\u043e\u043b\u044c\u043a\u0443 \u043e\u043d \u043e\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u0435\u0442 \u0433\u043e\u0440\u0430\u0437\u0434\u043e \u0431\u043e\u043b\u044c\u0448\u0435 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0439, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043c\u043e\u0447\u044c \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0432\u0430\u0448 \u043a\u043e\u0434 \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u043c \u0438 \u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u044b\u043c \u043f\u0440\u0438 \u0441\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0438 \u0441\u043b\u043e\u0436\u043d\u044b\u0445 \u043f\u043e\u0442\u043e\u043a\u043e\u0432 \u0434\u0430\u043d\u043d\u044b\u0445.<\/p>\n<\/div>\n<p> \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\/post\/568064\/\"> https:\/\/habr.com\/ru\/post\/568064\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u2014 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 Ben Lesh&nbsp;<a href=\"https:\/\/benlesh.com\/posts\/learning-observable-by-building-observable\/\" rel=\"noopener noreferrer nofollow\"><u>\u201cLearning Observable By Building Observable\u201d<\/u><\/a>. \u0422\u0430\u043a\u0436\u0435 \u044f \u0432\u0435\u0434\u0443 \u0442\u0435\u043b\u0435\u0433\u0440\u0430\u043c \u043a\u0430\u043d\u0430\u043b \u201c<a href=\"https:\/\/t.me\/frontend_pasta\" rel=\"noopener noreferrer nofollow\"><u>Frontend \u043f\u043e-\u0444\u043b\u043e\u0442\u0441\u043a\u0438<\/u><\/a>\u201d, \u0433\u0434\u0435 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e \u043f\u0440\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u044b\u0435 \u0432\u0435\u0449\u0438 \u0438\u0437 \u043c\u0438\u0440\u0430 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u043e\u0432 \u200b<\/p>\n<h2>\u0412\u0441\u0442\u0443\u043f\u043b\u0435\u043d\u0438\u0435<\/h2>\n<p>\u042d\u0442\u043e \u043f\u043e\u0432\u0442\u043e\u0440\u0435\u043d\u0438\u0435 \u0441\u0442\u0430\u0440\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438, \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043d\u043e\u0439 \u043c\u043d\u043e\u044e \u0432 2016 \u0433\u043e\u0434\u0443, \u0438 \u0434\u043e\u043a\u043b\u0430\u0434\u0430, \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u044f \u0432\u044b\u0441\u0442\u0443\u043f\u0430\u043b \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437. \u042f \u0445\u043e\u0447\u0443 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043c\u043e\u0434\u0435\u0440\u043d\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043d\u0442\u0435\u043d\u0442 \u0438, \u043d\u0430\u0434\u0435\u044e\u0441\u044c, \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0435\u0433\u043e. \u0426\u0435\u043b\u044c \u2014 \u043f\u043e\u043c\u043e\u0447\u044c \u043b\u044e\u0434\u044f\u043c \u043f\u043e\u043d\u044f\u0442\u044c, \u0447\u0442\u043e \u0442\u0430\u043a\u043e\u0435 observable. \u041d\u0435 \u0442\u043e\u043b\u044c\u043a\u043e observable \u0438\u0437 RxJS, \u043d\u043e \u0438 \u043b\u044e\u0431\u043e\u0439 observable (\u0434\u0430, \u0438\u0445 \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0434\u043d\u043e\u0433\u043e) \u043a\u0430\u043a \u0442\u0438\u043f.<\/p>\n<h3>Observables \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438<\/h3>\n<p>\u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c \u044d\u0442\u043e, \u044f \u0445\u043e\u0447\u0443 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0440\u044f\u0434\u043e\u043c \u0434\u0432\u0430 \u043f\u0440\u0438\u043c\u0435\u0440\u0430. \u041e\u0434\u0438\u043d \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f observable \u0438\u0437 RxJS, \u0430 \u0434\u0440\u0443\u0433\u043e\u0439 \u2014 \u043f\u0440\u043e\u0441\u0442\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439. \u041e\u0431\u0430 \u044d\u0442\u0438\u0445 \u043f\u0440\u0438\u043c\u0435\u0440\u0430 \u0438\u043c\u0435\u044e\u0442 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<p><strong>RxJS Observable<\/strong><\/p>\n<p>\u042d\u0442\u043e \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u0440\u043e\u0441\u0442\u043e\u0433\u043e observable, \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u043e\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e RxJS, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u044b\u0434\u0430\u0435\u0442 \u0442\u0440\u0438 \u0447\u0438\u0441\u043b\u0430 \u0438 \u0437\u0430\u0432\u0435\u0440\u0448\u0430\u0435\u0442\u0441\u044f. \u0415\u0441\u043b\u0438 \u0432\u044b \u0443\u0436\u0435 \u0437\u043d\u0430\u043a\u043e\u043c\u044b \u0441 RxJS, \u044d\u0442\u043e \u044d\u043a\u0432\u0438\u0432\u0430\u043b\u0435\u043d\u0442 (1, 2, 3).<\/p>\n<pre><code class=\"javascript\">import { Observable } from 'rxjs';   const source = new Observable&lt;number&gt;((subscriber) =&gt; {   subscriber.next(1);   subscriber.next(2);   subscriber.next(3);   subscriber.complete(); });   \/\/ Usage console.log('start'); source.subscribe({   next: console.log,   complete: () =&gt; console.log('done'), }); console.log('stop');<\/code><\/pre>\n<p><strong>\u0424\u0443\u043d\u043a\u0446\u0438\u044f \u201cobservable\u201d<\/strong><\/p>\n<pre><code class=\"javascript\">\/**  * A simple object with a `next` and `complete` callback on it.  *\/ interface Observer&lt;T&gt; {   next: (value: T) =&gt; void;   complete: () =&gt; void; }   \/**  * A function that takes a simple object with callbacks  * and does something them.  *\/ const source = (subscriber: Observer&lt;number&gt;) =&gt; {   subscriber.next(1);   subscriber.next(2);   subscriber.next(3);   subscriber.complete(); };   \/\/ Usage console.log('start'); source({   next: console.log,   complete: () =&gt; console.log('done'), }); console.log('stop');<\/code><\/pre>\n<p><strong>\u0412\u044b\u0432\u043e\u0434 (\u043e\u0431\u043e\u0438\u0445!)<\/strong><\/p>\n<pre><code>\"start\" 1 2 3 \"done\" \"stop\"<\/code><\/pre>\n<p>\u042f \u0445\u043e\u0447\u0443, \u0447\u0442\u043e\u0431\u044b \u0432\u044b \u043e\u0431\u0440\u0430\u0442\u0438\u043b\u0438 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0441\u0445\u043e\u0434\u0441\u0442\u0432\u043e. \u0412 \u043e\u0431\u043e\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u044b \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0435 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043c\u0435\u0442\u043e\u0434\u0430 next \u0438 complete. \u0412 \u043e\u0431\u043e\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0432\u044b \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442\u0435 next \u0438 complete \u0432 \u0442\u0435\u043b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u0412 \u043e\u0431\u043e\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u044f\u0445 \u0442\u0435\u043b\u043e \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0435\u0442\u0441\u044f \u0434\u043e \u0442\u0435\u0445 \u043f\u043e\u0440, \u043f\u043e\u043a\u0430 \u0432\u044b \u043d\u0435 \u0432\u044b\u0437\u043e\u0432\u0435\u0442\u0435 source.subscribe() \u0438\u043b\u0438 \u043f\u0440\u043e\u0441\u0442\u043e \u0432\u044b\u0437\u043e\u0432\u0435\u0442\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u043a\u0430\u043a source() \u0432 \u0434\u0440\u0443\u0433\u043e\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435. \u042d\u0442\u043e \u043f\u043e\u0442\u043e\u043c\u0443, \u0447\u0442\u043e observables \u2014 \u044d\u0442\u043e \u043f\u0440\u043e\u0441\u0442\u043e \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438.<\/p>\n<h3>\u041f\u043e\u0447\u0435\u043c\u0443 \u0431\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u0438? \u0427\u0442\u043e \u00ab\u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e\u0433\u043e\u00bb \u0432 observable?<\/h3>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e \u043f\u043e\u0434\u043c\u0435\u0447\u0435\u043d\u043e. \u0415\u0441\u043b\u0438 \u0431\u044b \u0432\u044b \u0431\u044b\u043b\u0438 \u043e\u0447\u0435\u043d\u044c \u043e\u0441\u0442\u043e\u0440\u043e\u0436\u043d\u044b, \u0432\u044b, \u0432\u0435\u0440\u043e\u044f\u0442\u043d\u043e, \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0434\u043b\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u0437 \u044d\u0442\u0438\u0445 \u0441\u043b\u0443\u0447\u0430\u0435\u0432. \u041d\u043e \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0432\u0435\u0440\u0441\u0438\u044f \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u043c\u0438 \u043d\u0435 \u0441\u043e\u0432\u0441\u0435\u043c \u00ab\u0431\u0435\u0437\u043e\u043f\u0430\u0441\u043d\u0430\u00bb. \u041d\u0430 \u043e\u0441\u043d\u043e\u0432\u0435 \u0432\u0441\u0435\u0433\u043e \u043b\u0438\u0448\u044c \u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u0430, \u0435\u0441\u043b\u0438 \u0441\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 observable \u0442\u0430\u043a\u043e\u0432\u0430, \u0447\u0442\u043e next \u043d\u0438\u043a\u043e\u0433\u0434\u0430 \u043d\u0435 \u0434\u043e\u043b\u0436\u0435\u043d \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f, \u043d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u044f\u0442\u0441\u044f \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0438 \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e.<\/p>\n<pre><code class=\"javascript\">const source = function (subscriber: Observer&lt;number&gt;) {   subscriber.next(1);   subscriber.next(2);   subscriber.next(3);   subscriber.complete();   subscriber.next(4); \/\/ \u0423\u043f\u0441, \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0431\u044b\u0442\u044c };<\/code><\/pre>\n<p>\u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0432\u044b\u0448\u0435 \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u043f\u0438\u0441\u0430\u043d\u043e \u00abdone\u00bb, \u0430 \u0437\u0430\u0442\u0435\u043c \u0441\u0440\u0430\u0437\u0443 4. \u0422\u0430\u043a\u043e\u0433\u043e \u0431\u044b\u0442\u044c \u0432\u043e\u043e\u0431\u0449\u0435 \u043d\u0435 \u0434\u043e\u043b\u0436\u043d\u043e! \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0445\u043e\u0442\u0438\u043c \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0441\u043f\u043e\u0441\u043e\u0431 \u0433\u0430\u0440\u0430\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c, \u0447\u0442\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043c\u0435\u0442\u043e\u0434 \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u0432\u0430\u043d \u043f\u043e\u0441\u043b\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f. \u042d\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0437\u0430\u043a\u043b\u044e\u0447\u0438\u0432 \u043d\u0430\u0448\u0443 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u043a\u043b\u0430\u0441\u0441.<\/p>\n<pre><code class=\"javascript\">\/**  * A class used to wrap a user-provided Observer. Since the  * observer is just a plain objects with a couple of callbacks on it,  * this type will wrap that to ensure `next` does nothing if called after  * `complete` has been called, and that nothing happens if `complete`  * is called more than once.  *\/ class SafeSubscriber&lt;T&gt; {   closed = false;     constructor(private destination: Observer&lt;T&gt;) {}     next(value: T) {     \/\/ Check to see if this is \"closed\" before nexting.     if (!this.closed) {       this.destination.next(value);     }   }     complete() {     \/\/ Make sure we're not completing an already \"closed\" subscriber.     if (!this.closed) {       \/\/ We're closed now.       this.closed = true;       this.destination.complete();     }   } }   \/**  * A class to wrap our function, to ensure that when the function is  * called with an observer, that observer is wrapped with a SafeSubscriber  *\/ class Observable&lt;T&gt; {   constructor(private _wrappedFunc: (subscriber: Observer&lt;T&gt;) =&gt; void) {}     subscribe(observer: Observer&lt;T&gt;): void {     \/\/ We can wrap our observer in a \"safe subscriber\" that     \/\/ does the work of making sure it's not closed.     const subscriber = new SafeSubscriber(observer);     this._wrappedFunc(subscriber);   } }   \/\/ Usage \/\/ Now 4 won't be nexted after we complete. const source = new Observable((subscriber) =&gt; {   subscriber.next(1);   subscriber.next(2);   subscriber.next(3);   subscriber.complete();   subscriber.next(4); \/\/ this does nothing. });<\/code><\/pre>\n<h3>\u041e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430 Partial Observers<\/h3>\n<p>\u0414\u0440\u0443\u0433\u043e\u0439 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0439 \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0439 \u2014 \u00abpartial\u00bb observer. \u0414\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u043b\u043e\u0432\u0430\u043c\u0438, observable, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u0442\u043e\u0434 next \u0438\u043b\u0438 complete (\u0438\u043b\u0438, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043c\u0435\u0442\u043e\u0434 \u043e\u0448\u0438\u0431\u043a\u0438, \u043d\u043e \u043c\u044b \u0432\u0435\u0440\u043d\u0435\u043c\u0441\u044f \u043a \u044d\u0442\u043e\u043c\u0443 \u043f\u043e\u0437\u0436\u0435). \u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u043b\u0435\u0433\u043a\u043e \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0441 \u044d\u0442\u0438\u043c \u0441\u0446\u0435\u043d\u0430\u0440\u0438\u0435\u043c \u0438\u043c\u0435\u044f \u043d\u0430\u0448 \u0442\u0438\u043f observable, \u0443\u043a\u0430\u0437\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u043e \u0432 \u043d\u0430\u0448\u0435\u043c SafeSubscriber:<\/p>\n<pre><code class=\"javascript\">class SafeSubscriber&lt;T&gt; {   closed = false;     constructor(private destination: Partial&lt;Observer&lt;T&gt;&gt;) {}     next(value: T) {     if (!this.closed) {       this.destination.next?.(value); \/\/ Note the ?. check here.     }   }     complete() {     if (!this.closed) {       this.closed = true;       this.destination.complete?.(); \/\/ And here.     }   } }<\/code><\/pre>\n<h2>\u0423\u0432\u0435\u0434\u043e\u043c\u043b\u0435\u043d\u0438\u044f \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435<\/h2>\n<p>\u0423\u0432\u0435\u0434\u043e\u043c\u0438\u0442\u044c \u043d\u0430\u0448\u0435\u0433\u043e \u043f\u043e\u0434\u043f\u0438\u0441\u0447\u0438\u043a\u0430 \u043e\u0431 \u043e\u0448\u0438\u0431\u043a\u0435 \u0442\u0430\u043a \u0436\u0435 \u043f\u0440\u043e\u0441\u0442\u043e, \u043a\u0430\u043a \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043a \u043d\u0430\u0448\u0438\u043c Observer \u0438 SafeSubscriber. \u0421\u0435\u043c\u0430\u043d\u0442\u0438\u043a\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u043e\u0445\u043e\u0436\u0430 \u043d\u0430 complete \u0432\u044b\u0448\u0435. \u0418 \u043e\u0448\u0438\u0431\u043a\u0430, \u0438 complete \u0441\u0447\u0438\u0442\u0430\u044e\u0442\u0441\u044f \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u0435\u043c \u043d\u0430\u0431\u043b\u044e\u0434\u0435\u043d\u0438\u044f.<\/p>\n<p>Observer \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u0441\u044f, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u044f\u0432\u0438\u043b\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u043e\u0448\u0438\u0431\u043e\u043a:<\/p>\n<pre><code>interface Observer&lt;T&gt; {   next: (value: T) =&gt; void;   complete: () =&gt; void;   error: (err: any) =&gt; void; }<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435 \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0432 \u043d\u0430\u0448 SafeSubscriber error \u043c\u0435\u0442\u043e\u0434:<\/p>\n<pre><code class=\"javascript\">class SafeSubscriber&lt;T&gt; {   closed = false;     constructor(private destination: Partial&lt;Observer&lt;T&gt;&gt;) {}     next(value: T) {     if (!this.closed) {       this.destination.next?.(value);     }   }     complete() {     if (!this.closed) {       this.closed = true;       this.destination.complete?.();     }   }     error(err: any) {     if (!this.closed) {       this.closed = true;       this.destination.error?.(err);     }   } }<\/code><\/pre>\n<h3>\u0418\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0438 \u0434\u0430\u043d\u043d\u044b\u0445 \u0438 \u043a\u0435\u0439\u0441 \u0434\u043b\u044f \u043e\u0442\u043c\u0435\u043d\u044b \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438<\/h3>\n<p>\u041e\u0441\u043d\u043e\u0432\u043d\u043e\u0439 \u0432\u0430\u0440\u0438\u0430\u043d\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f observable \u2014 \u043e\u0431\u0435\u0440\u0442\u044b\u0432\u0430\u043d\u0438\u0435 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e\u0433\u043e \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a\u0430 \u0434\u0430\u043d\u043d\u044b\u0445, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440 WebSocket.<\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0447\u0442\u043e-\u0442\u043e \u043f\u043e\u0434\u043e\u0431\u043d\u043e\u0435, \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u0430\u0448 \u0441\u0430\u043c\u043e\u0434\u0435\u043b\u044c\u043d\u044b\u0439 observable \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u044b\u0439 \u0432\u044b\u0448\u0435, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"javascript\">const helloSocket = new Observable&lt;string&gt;((subscriber) =&gt; {   \/\/ Open a socket.   const socket = new WebSocket('wss:\/\/echo.websocket.org');     socket.onopen = () =&gt; {     \/\/ Once it's open, send some text.     socket.send('Hello, World!');   };     socket.onmessage = (e) =&gt; {     \/\/ When it echoes the text back (in the case of this particular server)     \/\/ notify the consumer.     subscriber.next(e.data);   };     socket.onclose = (e) =&gt; {     \/\/ Oh! we closed!     if (e.wasClean) {       \/\/ ...because the server said it was done.       subscriber.complete();     } else {       \/\/ ...because of something bad. Maybe we lost network or something.       subscriber.error(new Error('Socket closed dirty!'));     }   }; });   \/\/ Start the websocket and log the echoes helloSocket.subscribe({   next: console.log,   complete: () =&gt; console.log('server closed'),   error: console.error, });<\/code><\/pre>\n<p>\u041d\u043e \u0442\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u043f\u043e\u044f\u0432\u0438\u043b\u0430\u0441\u044c \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430. \u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0434\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043d\u0430\u0448 observable, \u043d\u0435 \u0438\u043c\u0435\u0435\u0442 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0438 \u043e\u0442\u043c\u0435\u043d\u0438\u0442\u044c \u0435\u0433\u043e \u0438 \u0437\u0430\u043a\u0440\u044b\u0442\u044c \u0441\u043e\u043a\u0435\u0442. \u041d\u0430\u043c \u043d\u0443\u0436\u0435\u043d \u0441\u043f\u043e\u0441\u043e\u0431 \u043f\u0440\u0435\u043a\u0440\u0430\u0442\u0438\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443. \u0415\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u0431\u044b \u0432\u0435\u0440\u043d\u0443\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0449\u0443\u044e \u043d\u0430\u0448\u0443 \u043b\u043e\u0433\u0438\u043a\u0443 \u043f\u0440\u0435\u043a\u0440\u0430\u0449\u0435\u043d\u0438\u044f \u043f\u043e\u0434\u043f\u0438\u0441\u043a\u0438.<\/p>\n<pre><code class=\"javascript\">const source = (subscriber: Observer&lt;string&gt;) =&gt; {   const socket = new WebSocket('wss:\/\/echo.websocket.org');     socket.onopen = () =&gt; {     socket.send('Hello, World!');   };     socket.onmessage = (e) =&gt; {     subscriber.next(e.data);   };     socket.onclose = (e) =&gt; {     if (e.wasClean) {       subscriber.complete();     } else {       subscriber.error(new Error('Socket closed dirty!'));     }   };     return () =&gt; {     if (socket.readyState &lt;= WebSocket.OPEN) {       socket.close();     }   }; };   const teardown = source({   next: console.log,   complete: () =&gt; console.log('done'),   error: console.error, });   \/\/ Decide you really don't want to keep the socket open. teardown();<\/code><\/pre>\n<p>\u0415\u0449\u0435 \u043e\u0434\u043d\u0430 \u043f\u0440\u043e\u0431\u043b\u0435\u043c\u0430, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0432\u043e\u0437\u043d\u0438\u043a\u0430\u0435\u0442 \u043d\u0435 \u043e\u0431\u044f\u0437\u0430\u0442\u0435\u043b\u044c\u043d\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u0430\u044f \u0441 WebSocket, \u043d\u043e \u0438 \u0441 \u0434\u0440\u0443\u0433\u0438\u043c\u0438 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u043c\u0438, \u2014 \u044d\u0442\u043e \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438, \u043a\u043e\u0433\u0434\u0430 \u0430\u0432\u0442\u043e\u0440 observable \u0440\u0435\u0448\u0430\u0435\u0442, \u0447\u0442\u043e \u043e\u043d \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u043e\u0448\u0438\u0431\u043a\u043e\u0439 \u0438\u043b\u0438 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435\u043c \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u044f, \u0438 \u043e\u043d \u0445\u043e\u0447\u0435\u0442 \u0443\u0432\u0435\u0434\u043e\u043c\u0438\u0442\u044c \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f, \u0430 \u0437\u0430\u0442\u0435\u043c \u0443\u0434\u0430\u043b\u0438\u0442\u044c \u0435\u0433\u043e. \u0411\u044b\u043b\u043e \u0431\u044b \u043d\u0435\u043f\u043b\u043e\u0445\u043e \u0438\u043c\u0435\u0442\u044c \u0435\u0434\u0438\u043d\u043e\u0435 \u043c\u0435\u0441\u0442\u043e \u0434\u043b\u044f \u044d\u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438 \u0432\u044b\u0437\u043e\u0432\u0435 subscriber.error \u0438\u043b\u0438 subscriber.complete \u0443\u0434\u0430\u043b\u0435\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u043b\u043e\u0441\u044c \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0431\u044b\u0441\u0442\u0440\u0435\u0435.<\/p>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0438\u0442\u044c\u0441\u044f \u0432\u0441\u0435\u0433\u043e \u044d\u0442\u043e\u0433\u043e \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439 SafeSubscriber \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u0430 Subscription.<\/p>\n<pre><code class=\"javascript\">\/**  * Our subscription type. This is to manage teardowns.  *\/ class Subscription {   private teardowns = new Set&lt;() =&gt; void&gt;();     add(teardown: () =&gt; void) {     this.teardowns.add(teardown);   }     unsubscribe() {     for (const teardown of this.teardowns) {       teardown();     }     this.teardowns.clear();   } }   class SafeSubscriber&lt;T&gt; {   closed = false;     constructor(     private destination: Partial&lt;Observer&lt;T&gt;&gt;,     private subscription: Subscription,   ) {     \/\/ Make sure that if the subscription is unsubscribed,     \/\/ we don't let any more notifications through this subscriber.     subscription.add(() =&gt; (this.closed = true));   }     next(value: T) {     if (!this.closed) {       this.destination.next?.(value);     }   }     complete() {     if (!this.closed) {       this.closed = true;       this.destination.complete?.();       this.subscription.unsubscribe();     }   }     error(err: any) {     if (!this.closed) {       this.closed = true;       this.destination.error?.(err);       this.subscription.unsubscribe();     }   } }   class Observable&lt;T&gt; {<\/code><\/pre>\n<\/div>\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-326553","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/326553","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=326553"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/326553\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=326553"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=326553"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=326553"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}