DEV Community

Danil Poletavkin
Danil Poletavkin

Posted on

Создание своего собственного оператора в RxJS

Это вольный перевод очень хорошей статьи
https://netbasal.com/creating-custom-operators-in-rxjs-32f052d69457

В этом материале вместо слова наблюдаемый (Observable) используется слово издатель, а вместо слова наблюдатель (Observer) - слово подписчик.

Что такое оператор? Это издатель, который принимает на вход другого
издателя. Простейший оператор будет выглядеть так:

function myOperator<T>(source:Observable<T>){
return source;
}
Enter fullscreen mode Exit fullscreen mode

Этот оператор бесполезен, так как возвращает (или передаёт далее по
цепочке) того же издателя, что и получает, но тем не менее это
оператор!

Теперь давайте попробуем вернуть какого-нибудь другого издателя:

function myOperator<T>(source: Observable<T>){
return new Observable(subscriber=>{
subscriber.next(1);
subscriber.complete();
})
}
Enter fullscreen mode Exit fullscreen mode

Этот оператор никак не использует входного издателя, а просто возвращает единицу.

Теперь попробуем создать оператор, который будет отфильтровывать
значения null и undefined и не будет пускать их дальше по цепи.

function filterNil() {
    return function <T>(sourse: Observable<T>) {
        return new Observable(s => {
            sourse.subscribe(
                {
                    next: (v) => {
                        if (v!==undefined&&v!==null) {
                            s.next(v);
                        }
                    },
                    error: (error) => {
                        s.error();
                    },
                    complete: () => {
                        s.complete();
                    }
                }
            )
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

Почему мы обернули оператор в ещё одну функцию? Это для того, чтобы было
удобно передавать оператору какие-то аргументы. Теперь эта функция
возвращает оператор и вызов функции равносилен применению оператора.

Но что-то не так - мы только что создали утечку памяти. Каждый
издатель должен возвращать функцию unsubscribe(), которая выполняет все
необходимые по очистке памяти действия, а издатель в нашем операторе
такую функцию не возвращает. Для того, чтобы всё заработало как надо
добавим ещё одну строку

function filterNil() {
    return function <T>(sourse: Observable<T>) {
        return new Observable(s => {
            sourse.subscribe(
                {
                    next: (v) => {
                        if (v!==undefined&&v!==null) {
                            s.next(v);
                        }
                    },
                    error: (error) => {
                        s.error();
                    },
                    complete: () => {
                        s.complete();
                    }
                }
            )
            return () => s.unsubscribe(); <===
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

Конструкция Observable.subscribe() возвращает объект Subscription,
поэтому можно сократить код, показанный выше просто возвратив сам объект
подписки. Теперь при вызове unsubscribe() будет вызываться метод
unsubscribe() подписки, которую мы возвращаем.

function filterNil() {
    return function <T>(sourse: Observable<T>) {
        return new Observable(s => {
            return sourse.subscribe(
                {
                    next: (v) => {
                        if (v!==undefined&&v!==null) {
                            s.next(v);
                        }
                    },
                    error: (error) => {
                        s.error();
                    },
                    complete: () => {
                        s.complete();
                    }
                }
            )
        })
    }
}
Enter fullscreen mode Exit fullscreen mode

Рекомендуется создавать свои операторы на основе существующих и вот как
будет выглядеть оператор filterNil, созданный при помощи оператора
filter

function filterNil() {
    return function <T>(source: Observable<T>) {
        return source.pipe(filter(v=>v!==undefined && v!=null));
    }
}
Enter fullscreen mode Exit fullscreen mode

А так как операторы функции блока pipe сами по себе уже возвращают функции, то
есть оператор filter уже вернёт нужную нам функцию, принимающую и
возвращающую издателя, то можно сократить код до

function filterNil() {
return filter(v=>v!==undefined&&v!=null);
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)