Несколько наблюдаемых подписчиков - PullRequest
0 голосов
/ 06 мая 2018

Я пытаюсь заменить шаблон события, используя Observables. Общая идея заключается в том, чтобы иметь возможность отслеживать изменения в localStorage. Как показано ниже, для этого я создал Angular Injectable в качестве оболочки: StorageService. Работает как положено: элементы можно писать и читать. Если элемент обновляется, читатель также получает обновление. Однако, как только несколько читателей подписываются на один и тот же key, только один получает уведомление. Почему это происходит? Я ожидаю, что каждый экземпляр, считывающий элемент с ключа, использующего service.read('key'), в итоге получит одно и то же наблюдаемое. С показанным в конце тестом, похоже, это не так.

StorageService:

import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/concat';
import { Observer } from 'rxjs/Observer';

interface StorageItemsDictionary { [id: string]: any; }
interface ObserverObservableDictionary { [id: string]: { observer: Observer<any>, observable: Observable<any> }; };

@Injectable()
export class StorageService {

  cache: StorageItemsDictionary;
  ood: ObserverObservableDictionary;

  constructor() {
    this.cache = {};
    this.ood = {};
  }

  /**
   * Get item from current `key` and all further changes
   * @param key
   */
  public read<T>(key: string): Observable<T | null> {
    // assure there is an ObserverObservable pair for the key:
    this.assureOOPair(key);

    // create observable from the cache or localStorage if there is an item
    if (this.cache[key] === undefined) {
      const value = localStorage.getItem(key);
      const parsed = <T>JSON.parse(localStorage.getItem(key));
      this.cache[key] = parsed;
    }

    // one time observable for current item:
    const singleTimeObservable = new Observable(singleTimeObserver => {
      singleTimeObserver.next(this.cache[key] || null);
      singleTimeObserver.complete();
    });

    // merge it with the ood
    return <Observable<T | null>>Observable.concat(singleTimeObservable, this.ood[key].observable);
  }

  /**
   * Delete item from `key`
   * @param key
   */
  public delete(key: string): void {
    delete this.cache[key];
    this.ood[key].observer.next(null);
    localStorage.removeItem(key);
  }

  /**
   * Store `item` at `key` and notify all readers
   * @param key
   * @param item
   */
  public write<T>(key: string, item: T): void {
    this.assureOOPair(key);
    this.cache[key] = item;
    this.ood[key].observer.next(item); // notify others
    const stringified = JSON.stringify(item);
    localStorage.setItem(key, stringified);
  }

  /**
   * Create a ObserverObservable pair at `key` if it does not exist
   * @param key
   */
  private assureOOPair(key: string) {
    if (this.ood[key] === undefined) {
      this.ood[key] = {
        observable: null,
        observer: null
      };
      this.ood[key].observable = new Observable(observer => {
        this.ood[key].observer = observer;
      });
      this.ood[key].observable.subscribe(e => e); // to be able to apply next
    }
  }

}

Тест:

  it('multiple reads', async(inject([StorageService], (service: StorageService) => {
    localStorage.removeItem('key');
    let counter = 0;
    let counter2 = 0;
    service.write('key', 'value1');

    service.read('key').forEach(v => {
      console.log('multiple reads: 1', v);
      switch (counter) {
        case 0:
          expect(v).toEqual('value1');
          break;
        case 1:
          expect(v).toEqual('value2fail'); // this case gets never called. As a result, the test passes, even though it should fail here…
          break;
      }
      counter++;
    });

    service.read('key').forEach(v => {
      console.log('multiple reads: 2', v);
      switch (counter2) {
        case 0:
          expect(v).toEqual('value1');
          break;
        case 1:
          expect(v).toEqual('value2');
          break;
      }
      counter2++;
    });

    service.write('key', 'value2');
  })));

Журнал испытаний:

multiple reads: 1 value1
multiple reads: 2 value1
multiple reads: 2 value2

1 Ответ

0 голосов
/ 06 мая 2018

Я думаю, что вы ищете Тема . Темы похожи на наблюдаемые, но вы делитесь одними и теми же данными со всеми подписчиками. observable будет выполняться для каждой подписки. Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...