Как создать подмножество наблюдаемого на основе входного аргумента? - PullRequest
2 голосов
/ 04 марта 2020

Я знаю, что название может показаться странным, но я не уверен, как правильно express.

Позвольте мне объяснить пример использования:

У нас есть календарь с повторяющимися событиями (потенциально бесконечный список событий, повторяющихся, например, каждый понедельник навсегда)

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

Мне нужно обновить sh эти события для некоторых действий в приложении (например, пользователя изменил разрешение календаря, список календарей в своей системе, пользователь свернул и снова открыл приложение) - у меня есть потоки таких событий, как $appStatus $permissionStatus, $selectedCalendars

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

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

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


У меня есть несколько идей о том, как это сделать простым JS, но мне интересно, возможно ли создать решение Rx Js -i sh для этого

Мои идеи raw- js -rx-mix:

Когда запрашивается новый диапазон, я округляю его до полных недель. Затем я создаю новую наблюдаемую с общим значением для каждой недели и запоминаю ее на какой-то карте

например

const observablesMap = new Map();

function getObservableForDate(date: Date) {
  const weekStart = getStartOfWeek(date);
  // if we have 'cached' version of observable for this week - return cached one
  if (observablesMap.has(weekStart.getTime()) {
    return observablesMap.get(weekStart.getTime())
  }

  const weekObservable = // create observable that shares results

  // set it to the map
  // return it
}

Но после нескольких часов исследований я понятия не имею, буду ли и как мне это делать реализовать его в Rx JS способом.

Давайте предположим, что сигнатура функции извлечения равна fetchEvents(startDate, endDate)

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

1 Ответ

0 голосов
/ 06 марта 2020

Я создал работающее решение rx js на stackblitz. Я также добавлю код здесь на случай, если стекблиц отключится один раз в будущем.

Общая идея

  • Сохраните все запросы и решите, нужно ли вам новый запрос или нет
  • В зависимости от предыдущего решения поддельный или обрабатывающий http запрос
  • В зависимости от предыдущего типа запроса найдите существующий запрос или верните новый.

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

Интерфейсы

interface Requests {
  action: Action,
  currentRequest: number,
  accumulatedRequests: number[],
}

interface FulfilledRequest extends Requests{
  httpRequest: string
}

interface Response {
  accumulatedHttpResponses: {
    request: number,
    response: string
  }[],
  response: string
}

Значения по умолчанию

const defaultRequests: Requests = {
  action: Action.IgnoreRequest,
  currentRequest: -1,
  accumulatedRequests: []
}

const defaultResponseStorage: Response = {
  accumulatedHttpResponses: [],
  response: ''
}

Enum

enum Action {
  IgnoreRequest,
  ProcessRequest
}

Функции

const isUpdateAction = (action: Action) => action === Action.ProcessRequest

const fakeHttp = (date: number): Observable<string> => of('http response for: ' + date).pipe(
  tap(v => console.warn('fakeHttp called with: ', v))
);

const getResponseForExistingRequest = (storage: Response, request: FulfilledRequest): Response => {
  const index = storage.accumulatedHttpResponses.findIndex(response => response.request === request.currentRequest);
  return {
    accumulatedHttpResponses: storage.accumulatedHttpResponses,
    response: storage.accumulatedHttpResponses[index].response
  }
}

const getResponseForNewRequest = (storage: Response, request: FulfilledRequest): Response => {
  const newEntry = {request: request.currentRequest, response: request.httpRequest};
  return {
    accumulatedHttpResponses: [...storage.accumulatedHttpResponses, newEntry],
    response: request.httpRequest
  }
}

const getIgnoredRequest = (date: number, requests: Requests): Requests => ({
  currentRequest: date,
  action: Action.IgnoreRequest,
  accumulatedRequests: requests.accumulatedRequests
})

const getProcessedRequests = (date: number, requests: Requests): Requests => ({
  currentRequest: date,
  action: Action.ProcessRequest, 
  accumulatedRequests: [...requests.accumulatedRequests, date]
})

const processRequest = (requests: Requests, date: number): Requests => {
  const requestExists = requests.accumulatedRequests.some(request => request === date);
  return requestExists
    ? getIgnoredRequest(date, requests)
    : getProcessedRequests(date, requests)
  }

const processFulfilledRequest = (storage: Response, request: FulfilledRequest): Response => isUpdateAction(request.action)
  ? getResponseForNewRequest(storage, request)
  : getResponseForExistingRequest(storage, request)


const fulfillFakeRequest = (requests: Requests): Observable<FulfilledRequest> => of('').pipe(
  map(response => ({...requests, httpRequest: response})),
)

const fulfillHttpRequest = (requests: Requests): Observable<FulfilledRequest> => fakeHttp(requests.currentRequest).pipe(
  map(response => ({...requests, httpRequest: response}))
)

Окончательное подключение через наблюдаемые объекты

const date$: Subject<number> = new Subject();

const response$ = date$.pipe(
  scan(processRequest, defaultRequests),
  switchMap((requests): Observable<FulfilledRequest> => isUpdateAction(requests.action)
    ? fulfillHttpRequest(requests)
    : fulfillFakeRequest(requests)
  ),
  scan(processFulfilledRequest, defaultResponseStorage),
  map(response => response.response)
)
...