rxjs switchMap 缓存过时的结果并且不创建新流
rxjs switchMap cache the obsolete result and do not create new stream
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$
我的问题是我不能直接使用 rx
中的 ajax
之类的东西,因为 http 请求隐藏了第三方库,我能想到的解决方案是使用内联流,即每次创建新流
click$.pipe(
switchMap(() => from(fetch(`https://api.github.com/users?per_page=5`)))
).subscribe(e => {
console.log(e)
})
那么,我该如何解释这种行为以及处理这种情况的正确方法是什么?
一个问题是您在设置测试用例时实际上执行了 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
returns 一个承诺,它只解析一次。 from(<promise>)
然后不需要重新执行 ajax 调用,它只会发出已解析的值。
而 ajax.getJSON
returns 每次都重新执行的流。
如果用 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>
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$
我的问题是我不能直接使用 rx
中的 ajax
之类的东西,因为 http 请求隐藏了第三方库,我能想到的解决方案是使用内联流,即每次创建新流
click$.pipe(
switchMap(() => from(fetch(`https://api.github.com/users?per_page=5`)))
).subscribe(e => {
console.log(e)
})
那么,我该如何解释这种行为以及处理这种情况的正确方法是什么?
一个问题是您在设置测试用例时实际上执行了 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
returns 一个承诺,它只解析一次。 from(<promise>)
然后不需要重新执行 ajax 调用,它只会发出已解析的值。
而 ajax.getJSON
returns 每次都重新执行的流。
如果用 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>