Отменить новые подписки, если текущая не завершена. - PullRequest
0 голосов
/ 01 сентября 2018

У меня есть эпическое действие, которое выбирает компании с локального сервера, отправленного в методе componentDidMount компонента React. С помощью возвращаемых данных я создаю экземпляры моделей и передаю их редуктору, который выполняет свою работу.

Эпическое действие выглядит следующим образом:

export const fetchCompaniesEpic = (action$: Observable<Action>): Observable<Action> =>
    action$.pipe(
        ofType(FETCH_COMPANIES),
        delay(5000),
        switchMap((action: any) =>
            ajax(COMPANIES_RESOURCE_URL).pipe(
                map((data: any) =>
                    data.response.map((value: any) => new Company(value.symbol, value.name, value.lastSale, value.marketCap, value.sector, value.industry))
                ),
                map((companies: Company[]) => fetchCompaniesFulfilled(companies))
            )
        )
    );

Будучи довольно новым для redux-observable, я хотел бы задать вам 3 вопроса:

1) Каков наилучший способ отменить все новые подписки (и, следовательно, http-запросы), которые происходят между тем, пока текущая еще не завершена? (выполняется также в течение 5 секунд задержки)

2) Как лучше всего достичь цели? Я делаю какую-то плохую практику или что-то, что можно было бы сделать лучше? (rxjs, реагируй, редуцируй)

3) Как лучше подписаться, если я отправляю это действие? Что-то вроде dispatch({type: FETCH_COMPANIES}).subscribe

p.s. Это просто теоретический пример, потому что я хотел бы очень хорошо понять, как это работает, поэтому не зацикливайтесь на реальной полезности задержки и т. Д. *

Ответы [ 2 ]

0 голосов
/ 02 сентября 2018

Чтобы проиллюстрировать более подробную информацию о том, что написал @Xinan, вы можете рассмотреть следующий пример, имитирующий вашу ситуацию

const action$ = new Subject<number>();

const ajax = (delay: number) => {
    const ajax$ = new Subject<any>();
    setTimeout(() => {
        ajax$.next('delay ' + delay);
        ajax$.complete();
    }, delay);
    return ajax$.asObservable();
};

action$
.pipe(
    switchMap(delay => ajax(delay)),  // delay 1001, delay 1002
    // exhaustMap(delay => ajax(delay)),  // delay 3000, delay 1002
)
.subscribe(console.log);

setTimeout(() => {action$.next(3000);}, 0);  // action$ emission 1
setTimeout(() => {action$.next(1000);}, 1000);  // action$ emission 2 
setTimeout(() => {action$.next(1001);}, 1500); // action$ emission 3
setTimeout(() => {action$.next(1002);}, 3502); // action$ emission 4

Наблюдаемый action$ излучает 4 раза через 0, 1000, 1500 и 3502 миллисекунды соответственно.

Каждый раз, когда action$ излучает, мы создаем ajax Observable, который сам будет излучать только один раз, после задержки, указанной в параметре action$, а затем завершится.

Так, например, Наблюдаемая ajax, созданная первым уведомлением о action$ (то есть action $ эмиссия 1 ), испустит через 3 секунды и завершится, в то время как вторая ajax Наблюдаемые будут излучаться через 2 секунды (1 секунда из-за задержки action $ эмиссии 2 + 1 секунда из-за задержки, с которой ajax излучает).

В этой симуляции, если вы используете switchMap, на консоли вы увидите

delay 1001
delay 1002

Причина в том, что действие $ эмиссия 3 излучает, в то время как действие $ эмиссия 1 и действие $ эмиссии 2 все еще находятся на лету, и поэтому они завершены и отписался action $ эмиссии 3 по логике switchMap. После выброса action $ эмиссии 4 других action $missions больше нет, и поэтому он может завершиться нормально, так что на консоли будет напечатано diplay 1002.

Наоборот, если вы замените switchMap на exaustMap, вы получите

delay 3000
delay 1002

Причина - зеркало предыдущих рассуждений. действие $ эмиссия 2 и действие $ эмиссия 3 убиты, потому что действие $ эмиссия 1 все еще находится на лету.

0 голосов
/ 01 сентября 2018

Я предполагаю, что вам нужно exaustMap, в основном это противоположно switchMap

switchMap отменяет предыдущий и берет вместо него новый

exaustMap отменяет новый и поддерживает существующий, если это еще не сделано

...