Проблема с выводом оператора tap в RxJS - PullRequest
0 голосов
/ 29 марта 2020

Я запутался с выводом приведенного ниже кода, так как обработка take (3) должна прекратиться после рендеринга 10, но все еще получать 5,6 и 9 от оператора tap. Пожалуйста, обратитесь к выводу и фрагменту кода ниже.

  • Значение постукивания null
  • Значение постукивания 20
  • Элемент визуализации 20
  • Значение постукивания 15
  • Элемент визуализации 15
  • Полученное значение 10
  • Отображение элемента 10
  • Завершено
  • Полученное значение 5
  • Полученное значение 6
  • Полученное значение 9
of(null, 20, 15, 10, 5, 6, 9)
      .pipe(
        tap(val => console.log(`Tapped value ${val}`)),
        filterNil(),
        take(3)
      )
      .subscribe(
        item => console.log(`Rendering Item ${item}`),
        err => console.log(err),
        () => console.log('Completed')
      );
  }

const filterNil = () => (source: Observable<any>) =>
  new Observable(observer => {
    return source.subscribe({
      next(value) {
        if (value !== undefined && value !== null) {
          observer.next(value);
        }
      },
      error(error) {
        observer.error(error);
      },
      complete() {
        observer.complete();
      }
    });
  });

Ответы [ 2 ]

1 голос
/ 29 марта 2020

Окончательный ответ на этот вопрос

Правильный ответ на этот вопрос дает следующий пост .

Мой первый ответ - упрощенный c ответ

То, что вы видите, зависит от того, что ваш код является полностью синхронным, и, следовательно, unsubscribe после 3 излучений, что подразумевается в take(3), не имеет возможности бежать.

Посмотрите на эту версию

of(null, 20, 15, 10, 5, 6, 9)
      .pipe(
        delay(0),  // >>> intruduce a delay
        tap(val => console.log(`Tapped value ${val}`)),
        filterNil(),
        take(3)
  )

Здесь вы вводите delay, который дает take возможность unsubscribe, и, как следствие, вы видите поведение, которое вы ожидайте.

Не ответ, а более подробное обоснование

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

Начнем с того, что filterNil() является законным пользовательским оператором, который должен делать то же самое, что и filter(item => item !== null), где filter является оператором, предоставленным rxjs/operators, то есть библиотекой .

Теперь, если мы заменим filter(item => item !== null) на filterNil() в pipe, мы получим другой результат

of(null, 20, 15, 10, 5, 6, 9)
      .pipe(
        tap(val => console.log(`Tapped value ${val}`)),
        filter(item => item !== null),
        take(3)
      )

      .subscribe(
        item => console.log(`Rendering Item ${item}`),
        err => console.log(err),
        () => console.log('Completed')
      );
  }

// the output on the console is

Tapped value null
Tapped value 20
Rendering Item 20
Tapped value 15
Rendering Item 15
Tapped value 10
Rendering Item 10
Completed

Это означает, что filter(item => item !== null) и filterNil() не эквивалентно.

Тот факт, что они не эквивалентны, по-видимому, является следствием реализации subscribe метода соединения Observable с несколько иной природой filterNil и filter.

При использовании filterNil, след выполнения subscribe мето d of Observable это

enter image description here

Если, с другой стороны, мы используем оператор filter, трассировка выполнения метода subscribe Наблюдается это enter image description here

Следовательно, тот факт, что filterNil имеет атрибут operator, установленный на ноль, в то время как filter имеет атрибут operator, установленный на FilterOperator похоже, ведет другое поведение. Причины этого мне не ясны и стоят нового вопроса.

0 голосов
/ 29 марта 2020

Ваша filterNil функция все еще работает, потому что это новая наблюдаемая, на которую не влияет оператор take. Rx JS операторы работают по порядку. Вы можете переместить take в исходное положение в трубе, или я бы не рекомендовал создавать новую наблюдаемую в вашей функции фильтра

// operator position matters
.pipe(
  take(3)
  tap(val => console.log(`Tapped value ${val}`)),
  filterNil()
)

// Or re-factored filterNil operator

const filterNilRefactor = () => {
  return source => source.pipe(filter((value) => value !== undefined && value !== null))
}

.pipe(
  tap(val => console.log(`Tapped value ${val}`)),
  filterNilRefactor(),
  take(3)
)
...