Для чего нужен pipe в rxJS

104

Я думаю, что у меня есть базовая концепция, но есть некоторые неясности

В общем, я использую наблюдаемое:

observable.subscribe(x => {

})

Если я хочу отфильтровать данные, я могу использовать это:

import { first, last, map, reduce, find, skipWhile } from 'rxjs/operators';
observable.pipe(
    map(x => {return x}),
    first()
    ).subscribe(x => {

})

Я тоже могу это сделать:

import 'rxjs/add/operator/map';
import 'rxjs/add/operator/first';

observable.map(x => {return x}).first().subscribe(x => {

})

Итак, мои вопросы:

  1. В чем разница?
  2. Если нет разницы, почему существует функциональная труба?
  3. Почему этим функциям нужен другой импорт?
enno.void
источник
1
Я собирался сказать, что это для пользовательских, неродных операторов, но я даже не знаю, правильно ли это. Позволяет ли pipe()вам передавать операторы, которые вы создаете?
zero298 07

Ответы:

69

Операторы "pipable" (ранее "lettable") - это текущий и рекомендуемый способ использования операторов, начиная с RxJS 5.5.

Настоятельно рекомендую прочитать официальную документацию https://rxjs.dev/guide/v6/pipeable-operators

Основное отличие состоит в том, что проще создавать пользовательские операторы и лучше использовать древовидную структуру, не изменяя при этом какой-либо глобальный Observableобъект, который мог бы вызвать коллизии, если бы две разные стороны захотели создать оператор с тем же именем.

Использование отдельного importоператора для каждого оператора 'rxjs/add/operator/first'было способом сделать пакеты приложений меньшего размера. Импортируя только нужные вам операторы вместо всей библиотеки RxJS, вы можете значительно уменьшить общий размер пакета. Однако компилятор не может знать, импортировали ли вы, 'rxjs/add/operator/first'потому что он вам действительно нужен в вашем коде, или вы просто забыли удалить его при рефакторинге кода. Это одно из преимуществ использования конвейерных операторов, при которых неиспользуемый импорт автоматически игнорируется.

Мартин
источник
1
Что касается вашего подтверждения unused imports are ignored automatically, в настоящее время в IDE есть плагины, удаляющие неиспользуемый импорт.
silvanasono
Не все используют эти IDE или эти плагины, многие используют базовый текстовый редактор. Вероятно, большую часть времени мы не можем полагаться на утверждение, что каждый в команде использует ту же среду IDE / набор плагинов / текстовый редактор, что и мы.
Адам Фарина
3
@AdamFaryna: Конечно, некоторые команды могут писать код на бумаге, но зачем им это делать, если у них есть современные инструменты? Использование текстового редактора, особенно без важных плагинов, похоже на написание кода на бумаге. Вы можете это сделать, но зачем приличной команде / разработчику это делать?
Денес Папп
Редактор кода @DenesPapp не имеет значения, пока люди могут использовать его продуктивно. В остальном это просто личные предпочтения. Ваша аналогия с написанием кода на бумаге неточна: вы не можете выполнить код на бумаге, но код, написанный в любом текстовом редакторе, может быть выполнен.
Адам Фарина
1
@perymimon Вы можете, но вам нужно установить rxjs-compatпакет github.com/ReactiveX/rxjs/blob/master/docs_app/content/guide/v6/…
Мартин
16

Трубный метод

Согласно оригинальной документации

оператор pipable заключается в том, что функция принимает наблюдаемые в качестве входных данных и возвращает другой наблюдаемый. Предыдущий наблюдаемый остается неизменным.

pipe(...fns: UnaryFunction<any, any>[]): UnaryFunction<any, any>

Исходный пост

Что значит труба?

Это означает, что любые операторы, которые вы ранее использовали в экземпляре наблюдаемого, доступны как чистые функции в rxjs/operators. Это делает создание композиции операторов или повторное использование операторов очень простым, без необходимости прибегать ко всем видам программной гимнастики, когда вам нужно создать настраиваемое наблюдаемое расширение Observable, а затем перезаписать лифт, чтобы создать свою собственную вещь.

const { Observable } = require('rxjs/Rx')
const { filter, map, reduce,  } = require('rxjs/operators')
const { pipe } = require('rxjs/Rx')

const filterOutWithEvens = filter(x => x % 2)
const doubleByValue = x => map(value => value * x);
const sumValue = reduce((acc, next) => acc + next, 0);
const source$ = Observable.range(0, 10)

source$.pipe(
  filterOutWithEvens, 
  doubleByValue(2), 
  sumValue)
  .subscribe(console.log); // 50
Чанака Вирасингхе
источник
@VladKuts меняет коды и атрибуты. Приносим извинения за неудобства.
Чанака Вирасингхе
Спасибо, я даже не догадывался, что могу хранить операторы с поддержкой конвейера как ссылки на функции и использовать их в вызове pipe (). Это намного чище, чем всегда делать встроенным.
Алекс. A
9

Вот хорошее резюме, которое я придумал:

Он отделяет операции потоковой передачи (отображение, фильтрация, уменьшение ...) от основных функций (подписка, конвейерная передача). Связывание операций по конвейеру вместо цепочки не загрязняет прототип Observable, что упрощает встряхивание дерева.

См. Https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md#why

Проблемы с исправленными операторами для цепочки точек:

Любая библиотека, которая импортирует оператор патча, будет дополнять Observable.prototype для всех потребителей этой библиотеки, создавая слепые зависимости. Если библиотека прекращает их использование, они неосознанно ломают всех остальных. В случае с конвейерами вам необходимо импортировать нужные операторы в каждый файл, в котором вы их используете.

Операторы, запатченные непосредственно на прототипе, не подвержены «встряхиванию дерева» с помощью таких инструментов, как rollup или webpack. Конвейерные операторы будут такими, какими они являются просто функциями, напрямую извлеченными из модулей.

Неиспользуемые операторы, импортируемые в приложения, не могут быть надежно обнаружены никакими инструментами сборки или правилом lint. Это означает, что вы можете импортировать сканирование, но прекратить его использовать, и оно все еще добавляется в ваш выходной пакет. С конвейерными операторами, если вы их не используете, правило lint может подобрать его за вас.

Функциональная композиция потрясающая. Создание собственных пользовательских операторов становится намного проще, и теперь они работают и выглядят так же, как и все другие операторы из rxjs. Вам больше не нужно расширять Observable или отменять подъем.

Хуан Мендес
источник
8

В чем разница? Как вы видите в своем примере, основное отличие заключается в улучшении читабельности исходного кода. В вашем примере всего две функции, но представьте, есть ли их дюжина? тогда это будет как

function1().function2().function3().function4()

это действительно становится уродливым и трудным для чтения, особенно когда вы заполняете внутренние функции. Вдобавок к этому некоторые редакторы, такие как код Visual Studio, не допускают длины более 140 строк. но если это пойдет следующим образом.

Observable.pipe(
function1(),
function2(),
function3(),
function4()
)

Это значительно улучшает читаемость.

Если нет разницы, почему существует функциональная труба? Функция PIPE () предназначена для объединения все функции, которые принимают и возвращают наблюдаемое. Первоначально он принимает наблюдаемый объект, затем этот наблюдаемый объект используется во всей функции pipe () каждой функцией, используемой внутри него.

Первая функция принимает наблюдаемый объект, обрабатывает его, изменяет его значение и переходит к следующей функции, затем следующая функция принимает наблюдаемый выход первой функции, обрабатывает его и переходит к следующей функции, затем продолжается до тех пор, пока все функции внутри функции pipe () используйте это наблюдаемое, наконец, у вас есть обработанный наблюдаемый. В конце вы можете выполнить наблюдаемый объект с помощью функции subscribe (), чтобы извлечь из него значение. Помните, что значения в исходном наблюдаемом не меняются. !! 

Почему этим функциям нужен другой импорт? Импорт зависит от того, где функция указана в пакете rxjs. Это так. Все модули хранятся в папке node_modules в Angular. импортировать {класс} из "модуля";

В качестве примера возьмем следующий код. Я только что написал это в stackblitz. Таким образом, ничего не создается автоматически и не копируется откуда-то еще. Я не вижу смысла копировать то, что указано в документации rxjs, когда вы тоже можете пойти и прочитать это. Я предполагаю, что вы задали этот вопрос здесь, потому что не поняли документацию. 

  • Существуют классы pipe, observable, of, map, импортированные из соответствующих модулей. 
  • В теле класса я использовал функцию Pipe (), как показано в коде. 
  • Функция Of () возвращает наблюдаемый объект, который последовательно выдает числа при подписке.

  • Observable еще не подписан.

  • Когда вы использовали его как Observable.pipe (), функция pipe () использует данный Observable в качестве входных данных.

  • Первая функция, функция map (), использует этот Observable, обрабатывает его, возвращает обработанный Observable обратно в функцию pipe (),

  • затем обработанный Observable передается следующей функции, если таковая имеется,

  • и так продолжается до тех пор, пока все функции не обработают Observable,

  • в конце, что Observable возвращается функцией pipe () переменной, в следующем примере его obs.

Теперь дело в Observable: пока наблюдатель не подписался на него, он не выдает никакого значения. Поэтому я использовал функцию subscribe (), чтобы подписаться на этот Observable, как только я подписался на него. Функция of () начинает выдавать значения, затем они обрабатываются функцией pipe (), и в конце вы получаете окончательный результат, например, 1 берется из функции of (), 1 добавляется 1 в функции map (), и вернулся обратно. Вы можете получить это значение в качестве аргумента внутри функции subscribe (function ( argument ) {}).

Если вы хотите его распечатать, используйте как

subscribe( function (argument) {
    console.log(argument)
   } 
)
    import { Component, OnInit } from '@angular/core';
    import { pipe } from 'rxjs';
    import { Observable, of } from 'rxjs';
    import { map } from 'rxjs/operators';
    
    @Component({
      selector: 'my-app',
      templateUrl: './app.component.html',
      styleUrls: [ './app.component.css' ]
    })
    export class AppComponent implements OnInit  {
    
      obs = of(1,2,3).pipe(
      map(x => x + 1),
      ); 
    
      constructor() { }
    
      ngOnInit(){  
        this.obs.subscribe(value => console.log(value))
      }
    }

https://stackblitz.com/edit/angular-ivy-plifkg

Дон Диланга
источник