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

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

Как вы могли догадаться по названию, это приводит нас к RxJS. RxJS - это

- это набор библиотек для создания асинхронных программ, основанных на событиях, с использованием наблюдаемых коллекций

как взято из их Github. Что это на самом деле означает? Это классная библиотека, которая позволяет вам работать с изменениями объектов в реальном времени, без необходимости иметь дело со слушателями или адскими обратными вызовами. ДАВАЙ СДЕЛАЕМ ЭТО. Я сделаю UserService в качестве примера.

var rx = require('rx');
function UserService() {
}

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

var colors = [
  ‘red’,
  ‘orange’,
  ‘yellow’,
  ‘green’,
  ‘blue’,
  ‘indigo’,
  ‘purple’
];
var UserService = function() {
  var self = this;
 
  /* Variables */
  self.user = new rx.Subject();
 
  /* Functions */
  self.refresh = refresh;
 
  function refresh() {
    self.user.onNext({
      name: ‘Fake Name’,
      favoriteColor: colors[Math.floor(Math.random() * colors.length)] 
    });
  }
}

Отлично, теперь давайте посмотрим, что произойдет, если мы подпишемся на пользователя и обновимся несколько раз;

var userService = new UserService();
userService.user.subscribe((user) => {
  console.log('user updated:', user);
});
userService.refresh();
userService.refresh();

Вы должны увидеть что-то вроде

> user updated: { name: ‘Fake Name’, favoriteColor: ‘yellow’ }
> user updated: { name: ‘Fake Name’, favoriteColor: ‘purple’ }

Хороший. Так что это значит? Это означает, что, подписавшись на синглтон userService, мы можем получать обновления, когда кто-нибудь обновляет его. Если у вас есть, скажем, строка меню с именем пользователя в его любимом цвете, например, и пользователь обновляет свой любимый цвет в настройках, если меню подписано на службу пользователя (что должно быть…), тогда он немедленно получит обновления, сделанные на странице настроек, и изменит цвет. Отлично, правда?

Вы спросите, как бы это выглядело? Что-то вроде этого;

function someAsynchronousServerUpdate(user, callback) {
  if (user.favoriteColor !== 'purple') {
    callback(new Error('y u no like purple?!'));
  } else {
    callback(null, user);
  }
}
var UserService = function() {
  var self = this;
 
  /* Variables */
  self.user = new rx.Subject();
 
  /* Functions */
  self.refresh = refresh;
  self.update = update;
 
  function refresh() {
    self.user.onNext({
      name: ‘Fake Name’,
      favoriteColor: colors[Math.floor(Math.random() * colors.length)] 
    });
  }
  function update(_user) {
    someAsychronousServerUpdate(_user, (err, _validatedUser) {
      if (err) {
        // Do your error handling here
      } else {
        self.user.onNext(_validatedUser)
      }
    }
  }
}

Итак, теперь у нас есть встроенная проверка. Coolio. Но есть проблема. Прямо сейчас, когда мы подписываемся на пользователя в userService, мы не возвращаем пользователя, пока кто-то не обновит или не вызовет обновление. Это немного странно, но легко поправимо, вместо обычного Subject мы будем использовать BehaviorSubject.

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

function someAsynchronousServerUpdate(user, callback) {
  if (user.favoriteColor !== 'purple') {
    callback(new Error('y u no like purple?!'));
  } else {
    callback(null, user);
  }
}
var UserService = function() {
  var self = this;
 
  /* Variables */
  self.user = new rx.BehaviorSubject(null);
 
  /* Functions */
  self.refresh = refresh;
  self.update = update;
  /* Initialization Logic */
  self.refresh();
 
  function refresh() {
    self.user.onNext({
      name: ‘Fake Name’,
      favoriteColor: colors[Math.floor(Math.random() * colors.length)] 
    });
  }
  function update(_user) {
    someAsychronousServerUpdate(_user, (err, _validatedUser) {
      if (err) {
        // Do your error handling here
      } else {
        self.user.onNext(_validatedUser)
      }
    }
  }
}

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