Как работает оператор from в RxJS

Оператор RxJS from используется для преобразования итерируемых данных в наблюдаемые объекты. Это свойство может быть особенно полезно, если вы хотите превратить типы передаваемых и совместно используемых данных в наблюдаемые последовательности или же если функция собирается получить наблюдаемый объект и воздействовать на него. Также оператор from можно использовать, если вам нужно применить оператор RxJS, который обычно недоступен для исходного типа данных.

К итерируемым типам, которые можно преобразовать в наблюдаемые объекты с помощью from, относятся массивы, карты, наборы, промисы, узлы DOM и функции-генераторы. Ниже мы рассмотрим примеры некоторых из этих типов.

Массивы и оператор from

Чаще всего оператор from используется для преобразования массива в наблюдаемый объект:

let myArr = ['🐦', '😺', '🐕', '🐊'];

Rx.Observable
  .from(myArr)
  .filter(x => x !== '🐦')
  .map(x => `Hello ${x}!`)
  .subscribe(console.log);

  // Hello 😺!
  // Hello 🐕!
  // Hello 🐊!

Синхронные и асинхронные наблюдаемые объекты

По умолчанию оператор from возвращает синхронные наблюдаемые объекты:

let myArr = ['😺', '🐕', '🐊'];

console.log('Before');
Rx.Observable
  .from(myArr)
  .map(x => `Hello ${x}!`)
  .subscribe(console.log);
console.log('After');

// Before
// Hello 😺!
// Hello 🐕!
// Hello 🐊!
// After

Однако при необходимости их можно сделать асинхронными при помощи Rx.Scheduler.async:

let myArr = ['😺', '🐕', '🐊'];

console.log('Before');
Rx.Observable
  .from(myArr, Rx.Scheduler.async)
  .map(x => `Hello ${x}!`)
  .subscribe(console.log);
console.log('After');

// Before
// After
// Hello 😺!
// Hello 🐕!
// Hello 🐊!

Функции-генераторы и оператор from

Читайте также: Функции-генераторы в JavaScript ES6/ES2015

Функции-генераторы относятся к итерируемым типам данных, потому их легко можно преобразовать в наблюдаемый объект при помощи оператора from:

function* generateUnique() {
  let num = 0;
  while (true) {
    yield num++;
  }
}

Rx.Observable.from(generateUnique())
  .take(3)
  .subscribe(console.log);

  // 0
  // 1
  // 2

Примечание: Здесь мы используем оператор take для завершения наблюдаемого объекта после указанного количества значений. В противном случае у нас получился бы бесконечный наблюдаемый объект и при подписке случилась бы ошибка.

А вот немного более сложный пример, в котором также используется оператор zip (для объединения значений нескольких наблюдаемых объектов):

function* generateName() {
  yield 'Cat';
  yield 'Dog';
  yield 'Bird';
  return;
}

function* generateEmoji() {
  yield '😺';
  yield '🐕';
  yield '🐦';
  return;
}

function* generateSound() {
  yield 'Meow';
  yield 'Woof';
  yield 'Tweet';
  return;
}

const names = Rx.Observable.from(generateName());
const emojis = Rx.Observable.from(generateEmoji());
const sounds = Rx.Observable.from(generateSound());

const combined = Rx.Observable.zip(names, emojis, sounds, (name, emoji, sound) => {
  return `The ${name} ${emoji} goes ${sound.toUpperCase()}`;
})
.subscribe(console.log);

// The Cat 😺 goes MEOW
// The Dog 🐕 goes WOOF
// The Bird 🐦 goes TWEET

Примечание: Обратите внимание, что здесь для объединения генераторов и наблюдаемых объектов также используется оператор spawn, но в настоящее время в RxJS 5+ этот оператор недоступен.

Промисы и оператор from

Промисы также можно легко преобразовать в асинхронные наблюдаемые объекты, которые будут обертывать значения resolve или reject:

const myPromise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('Hello');
  }, 2000);
});

Rx.Observable
  .from(myPromise)
  .subscribe(x => console.log(x, ' World!'));

// Hello World! (after 2 seconds)

Узлы DOM

Давайте посмотрим на следующий простой пример, в котором набор из 3 узлов DOM преобразуется в наблюдаемый объект и сопоставляется для извлечения только textContent:

<h2>Hey,</h2>
<h2>Hello</h2>
<h2>Alligator!</h2>


<script>
  const h2s = document.querySelectorAll('h2');

  Rx.Observable.from(h2s)
    .map(h2 => h2.textContent)
    .subscribe(console.log);

    // Hey,
    // Hello
    // Alligator!
</script>

Кратко о строках

Строки тоже можно итерировать, следовательно, с ними можно использовать оператор from, но при этом каждый символ в строке будет отдельным значением:

Rx.Observable
  .from('Hi!')
  .subscribe(console.log);

  // H
  // i
  // !

Если вы хотите преобразовать строку в единое значение, вам нужно использовать оператор of, а не from:

Rx.Observable
  .of('Hi!')
  .subscribe(console.log);

  // Hi!
Tags:

Добавить комментарий