使用Rxjs与延迟并行地同时调用请求的请求块?

时间:2018-03-24 10:18:01

标签: javascript angular rxjs rxjs5

我有一个任务队列(长度为20个),其中每个任务都是一个要调用的ajax请求。

我想:

1)创建5个块(20/5 = 4个块)
2)执行每个块,其中块中的每个项将以1000毫秒的延迟执行。
3)当每个块项目完成时,等待3秒钟。

所以:

1 .. 1秒(↦绿色) .. 2 .. 1秒(↦绿色) .. 3 .. 1秒(↦绿色) .. 4 .. 1sec(↦green) .. 5 ..................... 3sec ..........
6 .. 1sec(↦绿色) .. 7 .. 1sec(↦green) .. 8 .. 1sec(↦green)。 .9 .. 1秒(↦绿色) .. 10 ..................... 3sec .. ........ ... 11 .. 1sec(↦绿色) .. 12 .. 1sec(↦green) .. 13 .. 1sec(↦green)。 .14 .. 1秒(↦绿色) .. 15 ..................... 3sec .. ........
16 .. 1sec(↦绿色) .. 17 .. 1sec(↦green) .. 18 .. 1sec(↦green)。 .19 .. 1秒(↦绿色) .. 20

我确实设法做了一些事情:

enter image description here

使用:

from(this.httpCodes)
      .pipe(bufferCount(5),
       concatMap((i, y) => from(i).pipe(mergeMap(f => {
                                    this.httpCodes[f.index].wasExecuted = true;
                                     return this.ajaxAlike(f.data).pipe(
                                                               catchError(() => { return of(-1) }),
                                                               map((r) => ({ res: r, data: f }))
                                                                      )
                                                      }) 
                                        ,delay(3000) )),

      )

但它并没有像我预期的那样执行。我没有看到块中每个项目之间的延迟

问题:

为什么我会看到这么多请求,如何更改我的代码,以便一个块中的每个项目都会以1秒的延迟执行(绿色应该在每秒后出现),并且 - 在每个块之后,等待3秒钟?

Online Demo

2 个答案:

答案 0 :(得分:4)

延迟运算符会延迟发出的项目。看起来你期望它发出物品,然后在发出下一个之前“睡眠”3秒钟。要实现这一目标,您可以连接空的延迟可观察

您可以创建以下可管道 睡眠运算符:

const sleep = ms => concat(Rx.Observable.empty().pipe(delay(ms)))

使用如下:

const {concatMap, concat, delay, bufferCount} = Rx.operators;

const sleep = ms => concat(Rx.Observable.empty().pipe(delay(ms)));

const ajaxAlike = call => Rx.Observable.of(call).pipe(delay(500));

Rx.Observable.range(0, 20).pipe(
  bufferCount(5),
  concatMap(calls => 
    Rx.Observable.from(calls).pipe(
      concatMap(call => ajaxAlike(call).pipe(sleep(1000))),
      sleep(3000)
    )
  )
)
.subscribe(console.log)
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.7/Rx.js"></script>

答案 1 :(得分:2)

编辑:好的,我想我现在明白你的意思了。我更新了小提琴,以便所有的块部分并行执行,但是块相互等待完成。所以这应该可以解决问题:

const chunkSize = 5;
const requestsWithDelay = httpRequests.map(obs => obs.delay(1000));

let chunks = [];
for (let i = 0; i < requestsWithDelay.length; i += chunkSize) {
    chunks.push(requestsWithDelay.slice(i, i + chunkSize));
}

const chunkedRequests = chunks.map(chunk => Rx.Observable.forkJoin(...chunk).delay(3000));
const parallelChunkRequest = Rx.Observable.concat(...chunkedRequests);
parallelChunkRequest.subscribe();

原始答案:

这样的东西会给你所需的延迟(给定httpRequests作为一个可观察的数组):

const requestsWithDelay = httpRequests.map((obs, idx) => { 
  let msDelay = 1000;
  if ((idx + 1) % 5 === 0 && idx < httpRequests.length - 1) {
    msDelay = 3000;
  }

  return obs.delay(msDelay);
});

const request = Rx.Observable.concat(...requestsWithDelay);

这应该有用,但不会有“实际”的可观察块。每个块中的请求不会并行执行(就像使用mergeMap一样),而是连续执行。

要获得httpRequests的可保存性,你可能会这样(但没有管道延迟):

const httpRequests = this.httpCodes.map(data => this.ajaxAlike(data));

但如果你想让这些块并行执行,你可以这样做:

let chunks = [];
for (let i = 0; i < requestsWithDelay.length; i += 5) {
    chunks.push(requestsWithDelay.slice(i, i + 5));
}

const chunkedRequests = chunks.map(chunk => Rx.Observable.concat(...chunk));
const parallelChunkRequest = Rx.Observable.merge(...chunkedRequests);

我创建了demo Fiddle

但是为什么你需要在每个块之后延迟3秒,如果它们并行执行而不是彼此等待?