使用RxJS如何缓冲函数调用,直到解决了另一个异步函数调用

时间:2018-08-26 01:11:09

标签: javascript asynchronous rxjs control-flow

如何使用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调用将不再被缓冲并立即执行。

我曾尝试过使用缓冲运算符,但无法达到预期的结果。

3 个答案:

答案 0 :(得分:1)

如果我理解正确,那么您的主要目标是通过一种机制来控制一系列功能的执行,该机制对它们进行缓冲,直到发生某种事情为止,而正是这种情况触发了缓冲功能的执行。

如果这是正确的,以下内容可能是解决您的问题的基础

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的通知,因此Observable是使用Subject构建的。换句话说,我们没有像这样funcToBuffer(1000)那样执行函数,而是创建了函数(作为对象)并要求functions$ Observable发出这样的函数

const aFunction = () => setTimeout(() => {console.log('I am a function that completes in 1 second');}, 1000);
functions$.next(aFunction);

通过这种方式,我们创建了最终将要执行的功能流。

第二件事,我们再次使用Subjects创建了另外两个Observable,buffer$executeBuffer$。当我们必须从functions$到目前为止发出的函数中创建一个缓冲区以及何时必须开始执行所缓冲的函数时,将使用此类Observable发出信号。

函数setBuffer中使用了这最后2个Observable。调用setBuffer时,基本上是说:请创建一个缓冲区,其中包含functions$到目前为止已发出的所有函数,并在指定参数executionDelay之后开始执行它们。 / p>

缓冲部分由使用functionBuffer$运算符创建的bufferWhen Observable执行。执行部分是利用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);

答案 1 :(得分:0)

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>

答案 2 :(得分:0)

我开始使用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>