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

Прежде чем обсуждать различия между Observable и Promise, давайте поговорим о том, что у них общего. И Observables, и Promises являются фреймворками для производства и использования данных. Они следуют протоколу push, что означает, что производитель точно определяет, когда отправлять данные потребителю. Для сравнения, в опрашивающем протоколе производитель производит данные только тогда, когда их запрашивает потребитель.

В обещании потребитель - это обратный вызов разрешения, который вы передаете конструктору при построении обещания, а производитель - это само обещание, которое вызывает обратный вызов асинхронно для передачи данных. В качестве примера, в приведенном ниже фрагменте, как только обещание (производитель) завершается через одну секунду, он вызывает обратный вызов (потребитель) и передает строку «Работа выполнена по обещанию» (данные).

const executor = (resolve, reject) => {
  // perform some work
  console.log("Work started on Promise");
  setTimeout(() => resolve("Work is done on Promise."), 1000);
};

const aPromise = new Promise(executor);
aPromise.then(value => console.log(value));

В Observable производитель является наблюдаемым, а его наблюдатели - потребителями.

// Producer
const anObservable = new Observable(subscriber => {
  console.log("Work started on Observable");
  setTimeout(() => {
    subscriber.next("Work is done on Observable");
    subscriber.complete();
  }, 1000);
});
// Consumer
anObservable.subscribe(
  value => console.log(value)
);

Хотя вы можете использовать Observable или Promise для создания и получения данных, Observable и Promise значительно отличаются друг от друга с точки зрения дизайна и функциональности, о чем мы поговорим в следующих разделах.

Многоразовое или одноразовое использование

Первое фундаментальное различие между Observable и Promise заключается в том, что Observable может выдавать несколько значений, тогда как Promise может выдавать только одно значение.

В приведенном ниже фрагменте кода наблюдатель выдает два значения и затем завершает.

const anObservable = new Observable(subscriber => {
  console.log("Observable started");
  subscriber.next(1);
  subscriber.next(2);
  subscriber.complete();
});

anObservable.subscribe(
  value => console.log(value),
  err => console.log(err),
  () => console.log("Observable completed")
);

Ниже представлен вывод в консоли:

Observable started
1
2
Observable completed

В отличие от Observable, Promise может генерировать только одно значение, как показано в приведенном ниже примере фрагмента.

const aPromise = new Promise((resolve, reject) => {
  console.log("Promise started");
  resolve(1);
  resolve(2);
});
aPromise.then(value => console.log(value));

Когда обещание вызывает обратный вызов разрешения в первый раз, потребитель получает первое значение через предложение then(). Однако второй вызов не имеет никакого эффекта, так как вы можете видеть только первое значение в выводе.

Promise started 
1

Нетерпеливый против ленивого

Еще одно различие между Observable и Promise состоит в том, что Observable ленив, а Promise нетерпелив.

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

const aPromise = new Promise((resolve, reject) => {
  // The line below gets executed right away
  console.log("Promise started");
  // The resolve() only gets call when the consumer wants the data (via the then() clause). 
  resolve(1);
});

Вот вывод консоли: Promise started

С другой стороны, Observable ленив, потому что он не начинает работу, пока первый наблюдатель на него не подпишется.

const anObservable = new Observable(subscriber => {
  // The line below does not get called until a subscriber subscribes to the observable. 
  console.log("Observable started");
  subscriber.next(1);
  subscriber.complete();
});

// anObservable.subscribe(value => console.log(value));

Чтобы увидеть результат, раскомментируйте последнюю строку кода, чтобы подписаться на наблюдаемое.

Ниже представлен результат:

Observable started 
1

Отменяемая или неотменяемая.

Следующее отличие состоит в том, что Observable можно отменить, а Promise - нет.

Поскольку Promise рассчитан на одноразовое использование, он не поддерживает отмену, по крайней мере, изначально. Для демонстрации рассмотрите приведенный ниже фрагмент:

const aPromise = new Promise((resolve, reject) => {
  console.log("Promise started");
  var counter = 1;
  const intervalId = setInterval(() => {
    console.log(counter++);
    if (counter > 10) {
      clearInterval(intervalId);
      resolve("Promise finished");
    }
  }, 1000);
});

Приведенный выше код имитирует длительную операцию, выполнение которой занимает 10 секунд, и каждую секунду выводит значение переменной счетчика. Во время выполнения, если вызывающий абонент решает отменить, вызывающий не имеет возможности отменить. Обещание выполняется полностью, пока не завершится. Вам нужно будет самостоятельно создать отмену или использовать стороннюю библиотеку.

Ниже приводится результат:

Promise started
1
2
3
4
5

Напротив, Observable изначально поддерживает отмену. Давайте изменим приведенный выше пример, чтобы использовать Observables, и посмотрим, как отменить операцию до ее завершения.

import { interval, Observable, Subscription } from "rxjs";

const anObservable = new Observable(subscriber => {
  console.log("Observable started");
  var counter = 1;
  const intervalId = setInterval(() => {
    if (subscriber.closed) {
      console.log("Received cancellation");
      clearInterval(intervalId);
      subscriber.complete();
    }
    subscriber.next(counter++);
    if (counter > 10) {
      subscriber.complete();
    }
  }, 1000);
});
// subscribe to receive values from the observable.
const subscription: Subscription = anObservable.subscribe(value =>
  console.log(value)
);
// cancel the subscription after 2 seconds
setInterval(() => subscription.unsubscribe(), 2000);

В приведенном выше фрагменте потребитель сигнализирует об отмене производителю, вызывая метод unsubscribe () в подписке. Если вы не знакомы с подпиской, она в основном предоставляет методы для управления и очистки ресурсов, связанных с подпиской на наблюдаемые объекты. Производитель может проверить запрос на отмену, проверив переменную closed. Ниже представлен результат:

Observable started
1
2
Received cancellation

Многоадресная рассылка против одноадресной

Еще одно различие между Promise и Observable заключается в том, что Observable может многоадресно передавать данные нескольким наблюдателям, тогда как Promise запускается только один раз и возвращает данные своему единственному потребителю.

Для демонстрации рассмотрите приведенный ниже фрагмент:

const aPromise = new Promise((resolve, reject) => {
  console.log("Promise started");
  setTimeout(() => resolve(1), 1000);
  setTimeout(() => resolve(2), 2000);
});
aPromise.then(value => console.log(value));
aPromise.then(value => console.log(value));

Вызов метода then() во второй раз не приводит к повторному выполнению обещания, поскольку обещание уже выполнено. Фактически, как только обещание выполнено, оно кэширует результат и возвращает кешированное значение для всех последующих вызовов метода then(), как показано в выходных данных ниже.

Promise started
1
1

Давайте теперь рассмотрим пример того, как наблюдаемые объекты могут быть многоадресными, что означает, что они могут транслировать данные нескольким подписчикам.

const anObservable = new Observable(subscriber => {
  console.log('Observable started.');
  setTimeout(() => subscriber.next(1), 1000);
  setTimeout(() => subscriber.next(2), 2000);
});
// each call to subscribe() results in a new Subscription
const subscription: Subscription = anObservable.subscribe(value => console.log(value));
const anotherSubscription: Subscription = anObservable.subscribe(value => console.log(value));

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

Observable started.
Observable started.
1
1
2
2

Синхронный и асинхронный

Наконец, в то время как Promise является асинхронным по своей природе, Observable может быть синхронным или асинхронным.

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

Как демонстрируют приведенные ниже фрагмент и выходные данные, хотя мы объявляем Promise перед Observable, выходные данные observable отображаются сначала перед выходными данными Promise.

import { interval, Observable, Subscription } from "rxjs";

const aPromise = new Promise((resolve, reject) => {
  resolve('Hi there. I\'m a Promise ');
});
aPromise.then(value => console.log(value));
const anObservable = new Observable((subscriber) => {
  subscriber.next('Hi there. I\'m an Observable');
})
anObservable.subscribe(value => console.log(value));

Ниже представлен результат:

Hi there. I'm an Observable
Hi there. I'm a Promise

Наблюдаемое также может быть асинхронным. Например, мы можем заключить вызов .next () в асинхронную функцию, такую ​​как setTimeout (), чтобы наблюдаемые объекты выдавали значение асинхронно.

import { interval, Observable, Subscription } from "rxjs";

const aPromise = new Promise((resolve, reject) => {
  setTimeout(() => resolve('Hi there. I\'m a Promise '), 500);
});
aPromise.then(value => console.log(value));
const anObservable = new Observable((subscriber) => {
  setTimeout(() => subscriber.next('Hi there. I\'m an Observable'), 500);
})
anObservable.subscribe(value => console.log(value));

Ниже представлен результат:

Hi there. I'm a Promise 
Hi there. I'm an Observable

Поскольку и Promise, и Observable выполняются асинхронно, и поскольку мы сначала объявляем обещание, выходные данные обещания отображаются в консоли перед выходными данными наблюдаемого, как и ожидалось.

Заключение

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

использованная литература

Наблюдаемый

Отмена обещаний в JavaScript

Мастер-класс: Основы реактивного взаимодействия с RxJS, день 1

Первоначально опубликовано на https://www.taithienbo.com 25 апреля 2021 г.