![](/img/trans.png)
[英]how to prevent/lock a function from returning until another separate async call has resolved?
[英]Using RxJS how to buffer function calls until an other async function call has resolved
如何使用 RxJS 来缓冲函数调用,直到另一个异步函数解决?
这是我想要完成的一个简单示例
function asyncFunc(time) { setTimeout(() => { console.log('asyncFunc has resolved'); }, time); } function funcToBuffer(time) { setTimeout(() => { console.log(time); }, time); } asyncFunc(3000); funcToBuffer(1000); funcToBuffer(2000); funcToBuffer(4000); funcToBuffer(5000); asyncFunc(8000); funcToBuffer(6000); funcToBuffer(7000);
目前此代码将打印:
1000
2000
asyncFunc has resolved
4000
5000
6000
7000
asyncFunc has resolved
我想要的是打印:
asyncFunc has resolved
1000
2000
4000
5000
asyncFunc has resolved
6000
7000
本质上,我想要某种控制流,允许我随时调用 funcToBuffer,但在幕后,我希望它在 asyncFunc 正在执行并等待解决时保持执行。 一旦 asyncFunc 解决,funcToBuffer 调用不应再被缓冲并立即执行。
我曾尝试使用缓冲区运算符,但无法达到预期的结果。
如果我理解正确,您的主要目标是通过一种机制来控制一系列函数的执行,该机制将它们缓冲直到发生某些事情,而某些事情正是触发缓冲函数执行的原因。
如果这是正确的,以下内容可能是您问题的可能解决方案的基础
const functions$ = new Subject<() => any>();
const buffer$ = new Subject<any>();
const executeBuffer$ = new Subject<any>();
const setBuffer = (executionDelay: number) => {
buffer$.next();
setTimeout(() => {
executeBuffer$.next();
}, executionDelay);
}
const functionBuffer$ = functions$
.pipe(
bufferWhen(() => buffer$),
);
zip(functionBuffer$, executeBuffer$)
.pipe(
tap(functionsAndExecuteSignal => functionsAndExecuteSignal[0].forEach(f => f()))
)
.subscribe();
让我解释一下代码。
首先,我们构建functions$
,即我们想要控制的函数的 Observable 。 Observable 是使用 Subject 构建的,因为我们希望能够以编程方式控制此类 Observable 的通知。 换句话说,不是像funcToBuffer(1000)
这样的函数执行,而是创建函数(作为对象)并要求functions$
Observable 发出这样的函数
const aFunction = () => setTimeout(() => {console.log('I am a function that completes in 1 second');}, 1000);
functions$.next(aFunction);
通过这种方式,我们创建了一个最终将被执行的函数流。
第二件事,我们再创建 2 个 Observables, buffer$
和executeBuffer$
,再次使用 Subjects。 这样的 Observable 用于在我们必须从目前由functions$
发出的函数中创建缓冲区时以及何时必须开始执行缓冲的函数时发出信号。
最后 2 个 Observable 在函数setBuffer
。 当您调用setBuffer
您基本上会说:请创建一个缓冲区,其中包含迄今为止由functions$
发出的所有函数,并在指定为参数的executionDelay
时间之后开始执行它们。
缓冲部分由functionBuffer$
Buffer functionBuffer$
Observable 执行,该functionBuffer$
使用bufferWhen
操作符创建。 执行部分是利用zip
运算符实现的,它允许我们根据executeBuffer$
Observable 的发射来设置函数的执行节奏。
您可以通过设置以下测试数据来测试上述代码。
let f: () => any;
setBuffer(3000);
f = () => setTimeout(() => {console.log('f1');}, 1000);
functions$.next(f);
f = () => setTimeout(() => {console.log('f2');}, 2000);
functions$.next(f);
f = () => setTimeout(() => {console.log('f4');}, 4000);
functions$.next(f);
f = () => setTimeout(() => {console.log('f5');}, 5000);
functions$.next(f);
setBuffer(8000);
f = () => setTimeout(() => {console.log('f6');}, 6000);
functions$.next(f);
f = () => setTimeout(() => {console.log('f7');}, 7000);
functions$.next(f);
setBuffer(16000);
等待两个 observable 触发的 CombineLatest。
const { of, combineLatest } = rxjs; const { delay } = rxjs.operators; let obs1$ = of(1).pipe(delay(1000)); let obs2$ = of(2).pipe(delay(2000)); let now = new Date(); combineLatest(obs1$, obs2$).subscribe(([obs1, obs2]) => { let ellapsed = new Date().getTime() - now.getTime(); console.log(`${obs1} - ${obs2} took ${ellapsed}`); });
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.2/rxjs.umd.min.js"></script>
我开始使用 combineLatest 开发一个解决方案,但认为一旦我对它进行了更多思考,BehaviorSubject 将是一个更好的解决方案。
const { BehaviorSubject } = rxjs; const { filter } = rxjs.operators; let finalised$ = new BehaviorSubject(false); function asyncFunc(time) { setTimeout(() => { console.log('asyncFunc has resolved'); if (!finalised$.getValue()) { finalised$.next(true); } }, time); } function funcToBuffer(time) { finalised$.pipe(filter(finalised => finalised)).subscribe(_ => { // Filter so only fire finalised being true setTimeout(() => { console.log(time); }, time); }); } asyncFunc(3000); funcToBuffer(1000); funcToBuffer(2000); funcToBuffer(4000); funcToBuffer(5000); asyncFunc(8000); funcToBuffer(6000); funcToBuffer(7000);
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.2.2/rxjs.umd.min.js"></script>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.