0

Say we have a big array and processing of each element in that array takes a long time (5s). We want to add some delay (2s) before processing of next element.

I've somewhat managed to achieve that behavior like that:

let arr = [1, 2, 3]
let i = 0

Rx.Observable.of(null)
.map(val => arr[i++])
.do(val => {
  console.log(val + ' preloop')
  let time = +new Date() + 5000; while (+new Date() < time) {}
  console.log(val + ' afterloop')
})
.delay(2000)
.flatMap(val => (i < arr.length) ? Rx.Observable.throw(null) : Rx.Observable.empty())
.retry()
.subscribe(console.log, console.log, () => console.log('completed'))

The output is as expected:

1 preloop
delay 5s
1 afterloop
delay 2s
2 preloop
...
completed

But this code is ugly, not reusable and buggy and doesn't comply with the philosophy of rx. What is the better way?

Note that the array (or it might even be not an array at all) is big and https://stackoverflow.com/a/21663671/2277240 won't work here.

The question is hypothetycal though I can think of some use cases.

grabantot
  • 2,111
  • 20
  • 31

2 Answers2

0

I'm not sure why the fact that the array is big important here, and you can solve your issue using the same method from the link you provided.

For your long operation a better practice would probably be a promise. I used an async sleep function to simulate a 5 second operation, you can replace it with your promise.

The trick for the extra delay, is to concat a dummy element, delay it and then ignore it.

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

Rx.Observable.from([1, 2, 3])
  .concatMap(item => 
    Rx.Observable.defer(() => sleep(5000))
      .mapTo(item)
      .concat(Rx.Observable.of(null).delay(2000).ignoreElements()) 
  )
  .subscribe(console.log, console.log, () => console.log('completed'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/5.5.2/Rx.js"></script>
ZahiC
  • 13,567
  • 3
  • 25
  • 27
  • `Rx.Observable.from([1, 2, 3]).concatMap(item => Rx.Observable.of(item).delay(2000)).subscribe(...)` seems to be enough. Any idea on how to avoid the delay before the first element and before completed (after the last element)? – grabantot Oct 28 '17 at 16:08
  • Try using the cocatMap function second parameter. It supposed to be the index of the item (item,index) => ... You can use it to build different observables for different cases – ZahiC Oct 28 '17 at 16:24
  • Thanks a lot. I hope you won't mind me editing your answer. – grabantot Oct 28 '17 at 16:39
  • @grabantot I really don't mind.. but.. First of all, you delay the item itself, and by this you also delay its processing. In my answer I tried not to delay the processing. Other than that, as I said in my answer - for most cases, long operations are better be a promise/async function. If not, your whole thread would be busy. Just to make sure, what kind of processing do you intend to do for each item? – ZahiC Oct 28 '17 at 17:04
  • Any suggestion to use a Promise (an observable that emits once and terminates) should be rejected out of hand. – Rick O'Shea Jan 22 '20 at 01:51
0

An alternative for delay which does not delay the first emit,

source
  .first()
  .merge(
    source.skip(1).delay(2000)
  )
Richard Matsen
  • 20,671
  • 3
  • 43
  • 77