Будет ли интервал, который был передан по конвейеру, работать в Rx JS? - PullRequest
1 голос
/ 17 июня 2020

Будет ли интервал, который был передан по конвейеру, работать в Rx JS?

Вот что я имею в виду. Предположим, у нас есть следующий код:

const arr = ["foo", "bar"];
const i = interval(500);
const toRun = i.pipe(
    map(index => arr[index]),
    take(arr.length)
);
toRun.subscribe(val => val);

Правильно ли я понимаю, что код работает следующим образом:

1 i создается, но не запускается пока мы не подпишемся на него.

2 С помощью метода pipe мы создаем новый Observable, который построен на i и работает следующим образом:

  • каждые 500 мс генерировать номер итерации (0, 1, 2, ...)
  • использовать номер итерации для извлечения значения из arr
  • передавать извлеченное значение кому бы то ни было был подписан на метод pipe result
  • остановить выдачу номеров итераций, когда номер итерации больше, чем arr.length

Таким образом, toRun.subscribe(val => val); выдаст foo, затем через 500 мс bar и перестанет работать. Хотя i никогда ничего не испускает, поскольку на него никто не подписался.

Я хочу понять, как это работает, поэтому, пожалуйста, исправьте мое объяснение, чтобы ответить на мой вопрос, если я ошибаюсь.

Я наткнулся на другой вопрос, работая с документацией Angular. Точнее через asyn c pipe . Я встретил там следующий пример:

import { Component } from '@angular/core';
import { Observable, interval } from 'rxjs';
import { map, take } from 'rxjs/operators';

@Component({
  selector: 'app-hero-message',
  template: `
    <h2>Async Hero Message and AsyncPipe</h2>
    <p>Message: {{ message$ | async }}</p>
    <button (click)="resend()">Resend</button>`,
})
export class HeroAsyncMessageComponent {
  message$: Observable<string>;

  private messages = [
    'You are my hero!',
    'You are the best hero!',
    'Will you be my hero?'
  ];

  constructor() { this.resend(); }

  resend() {
    this.message$ = interval(500).pipe(
      map(i => this.messages[i]),
      take(this.messages.length)
    );
  }
}

И мне стало любопытно, могут ли быть проблемы с производительностью из-за ненужного запуска interval(500) (поскольку pipe создаст новый наблюдаемый и interval(500) не будет использоваться явно, а только pipe во время создания нового наблюдаемого).

Ответы [ 2 ]

1 голос
/ 17 июня 2020

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

Есть много способов сделать что-то в RxJs; если вы начнете думать о способе Rx JS, вы найдете одно из правильных решений. В вашем случае эти два совета приведут вас в правильном направлении:

  • Где бы ни был массив для получения значений, подумайте о том, как извлечь из него наблюдаемые с помощью инструментов Rx Js, таких как from, of
  • Подумайте, как объединить более одной наблюдаемой и скоординировать их для получения правильного результата.

Замените присваивание toRun этим кодом:

 import {from, interval, zip} from "rxjs";
 // ...
 const toRun = zip(from(arr),i);  // where i is interval(500)

Это решение является простым, но прекрасным примером того, как вы используете эти два совета. Он намного аккуратнее и, вероятно, имеет лучшую производительность.

Под капотом from превращает массив в наблюдаемый, а zip использует частоту наблюдаемого интервала для «умеренного» вывода наблюдаемый массив. Подобное взаимодействие наблюдаемых объектов - это типичный паттерн, который вы очень часто будете видеть в способах работы Rx Js.

Примечания: В качестве бонуса использования zip в обработчике subscribe. вы получите не только значение интервала, но и соответствующее значение массива, которое передается в параметре массива.

1 голос
/ 17 июня 2020

Вы почти правильно поняли. Несколько примечаний:

  • Да, interval создает холодный наблюдаемый , который будет выдавать числа только при подписке.
  • pipe сам по себе не будет создавать новую наблюдаемую, в отличие от операторов в pipe.
  • map и take не делают наблюдаемый горячий, поэтому он все еще холодный и будет "работать" только при подписке.
  • interval изначально ожидает 500 мс перед отправкой foo (после подписки)
  • take откажется от подписки на наблюдаемый источник после заданного количества выбросов. Это приведет к тому, что каждый Observable в цепочке откажется от подписки на свой источник до тех пор, пока в конечном итоге не будет отменена подписка на interval. Значит, он больше не выдает числа.
...