rxjs switchMap кэширует устаревший результат и не создает новый поток - PullRequest
0 голосов
/ 12 февраля 2019
const s1$ = of(Math.random())
const s2$ = ajax.getJSON(`https://api.github.com/users?per_page=5`)
const s3$ = from(fetch(`https://api.github.com/users?per_page=5`))
const click$ = fromEvent(document, 'click')
click$.pipe(
    switchMap(() => s1$)
).subscribe(e => {
    console.log(e)
})

Я был смущен приведенным выше кодом и не могу рассуждать о них должным образом.В первом случае (s1$) каждый раз получен один и тот же результат, он мне смотрится хорошо, хотя я не могу понять, почему switchMap не запускает новый поток каждый раз.Хорошо, все в порядке

Действительно запутанная вещь случается, когда вы запускаете s2$ и s3$, выглядит эквивалентно, верно?НЕПРАВИЛЬНО!!!Поведение будет совершенно другим, если вы попробуете их!

Результат s3$ как-то кэшируется, т.е. если вы откроете сетевую панель, вы увидите, что запрос http был отправлен только ОДИН РАЗ.Для сравнения, запрос http отправляется каждый раз для s2$

Моя проблема заключается в том, что я не могу использовать что-то вроде ajax из rx напрямую, поскольку запрос http скрыт сторонней библиотекой,Решение, которое я могу придумать, состоит в том, чтобы использовать встроенный поток, то есть каждый раз создавать новый поток

click$.pipe(
    switchMap(() => from(fetch(`https://api.github.com/users?per_page=5`)))
).subscribe(e => {
    console.log(e)
})

Итак, как именно я могу объяснить такое поведение и как правильно справиться с этой ситуацией?

1 Ответ

0 голосов
/ 12 февраля 2019

Одна проблема заключается в том, что вы на самом деле выполняете Math.random и fetch при настройке тестового примера.

// calling Math.random() => using the return value
const s1$ = of(Math.random())

// calling fetch => using the return value (a promise)
const s3$ = from(fetch(`https://api.github.com/users?per_page=5`))

Другое: fetch возвращает обещание, которое разрешается только один раз.from(<promise>) тогда не нужно повторно выполнять вызов ajax, он просто выдаст разрешенное значение.

Принимая во внимание, что ajax.getJSON возвращает поток, который повторяется каждый раз.

Есливы оборачиваете тестовые потоки с помощью defer, вы получаете более интуитивное поведение.

const { of, defer, fromEvent } = rxjs;
const { ajax }                 = rxjs.ajax;
const { switchMap }            = rxjs.operators;

// defer Math.random()
const s1$ = defer(() => of(Math.random()));

// no defer needed here (already a stream)
const s2$ = ajax.getJSON('https://api.github.com/users?per_page=5');

// defer `fetch`, but `from` is not needed, as a promise is sufficient
const s3$ = defer(() => fetch('https://api.github.com/users?per_page=5'));

const t1$ = fromEvent(document.getElementById('s1'), 'click').pipe(switchMap(() => s1$));
const t2$ = fromEvent(document.getElementById('s2'), 'click').pipe(switchMap(() => s2$));
const t3$ = fromEvent(document.getElementById('s3'), 'click').pipe(switchMap(() => s3$));

t1$.subscribe(console.log);
t2$.subscribe(console.log);
t3$.subscribe(console.log);
<script src="https://unpkg.com/@reactivex/rxjs@6/dist/global/rxjs.umd.js"></script>

<button id="s1">test random</button>
<button id="s2">test ajax</button>
<button id="s3">test fetch</button>
...