Using Rxjs to invoke simultaneous chunks of requests in parallel with delay?

匿名 (未验证) 提交于 2019-12-03 08:57:35

问题:

I have a queue of tasks (20 in length) where each tasks is an ajax request to be invoked .

I want to :

1) Create chunks of 5 ( 20/5 =4 chunks)
2) Execute each chunk where each item in chunk will be executed with delay of 1000 ms.
3) When each chunks item complete , wait 3 seconds .

So :

1....2....3....4....5.....................3sec..........
6....7....8....9....10..................... 3sec.......... ... 11....12....13....14....15..................... 3sec..........
16....17....18....19....20

I did manage to do something close :

With :

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) )),        )

But it doesn't executed as I intended. I don't see delays between each item in chunk

Question:

Why do I see so many requests , And how can I change my code so that each item in a chunk will be executed with a 1 sec delay (green should be appear after each second) , and - after each chunk , wait 3 seconds?

Online Demo

回答1:

The delay operator delays an emitted item. It seems like you expect it to emit the item and then 'sleep' for 3 seconds before emitting the next. To achieve this you can concat an empty delayed observable.

You can create the following pipeable sleep operator:

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

And use it as follows:

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>


回答2:

EDIT: Ok, I guess now I see what you mean. I updated the fiddle, so that all chunk-parts get executed in parallel, but the chunks wait on each other to complete. So this should do the trick:

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();

Original Answer:

Something like this would give you the desired delays (given httpRequests as an array of observables):

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);

This should work, but there would not be "actual" chunks of observables. The requests in each chunk would not be executed in parallel (like with using mergeMap), but successively.

To get the abservable of httpRequests you could something like this (but without the delay in the pipe):

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

But if you want the chunks to execute in parallel you could do something like this:

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);

I created a demo Fiddle

But why do you need the 3 seconds delay after each chunk, if they get executed in parallel and don't wait for each other?



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!