rxjs switchMap кэширует устаревший результат и не создает новый поток
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 ответ
Одна проблема в том, что вы на самом деле выполняете 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>