How can I create an observable with a delay
Asked Answered
A

6

146

Question

For testing purposes, I'm creating Observable objects that replace the observable that would be returned by an actual http call with Http.

My observable is created with the following code:

fakeObservable = Observable.create(obs => {
  obs.next([1, 2, 3]);
  obs.complete();
});

The thing is, this observable emits immediatly. Is there a way to add a custom delay to its emission?


Track

I tried this:

fakeObservable = Observable.create(obs => {
  setTimeout(() => {
    obs.next([1, 2, 3]);
    obs.complete();
  }, 100);
});

But it doesn't seem to work.

Alarm answered 23/2, 2017 at 11:5 Comment(9)
github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/…Predictor
I tried to chain .create(...) with .delay(1000) but it didn't work: Observable_1.Observable.create(...).delay is not a function.Alarm
What exactly are you trying to accomplish?Predictor
are you subscribing to the observable?Engen
Fake the Http response delay with my own observable. @Engen yeah, the class I'm testing is calling the service (I'm trying to mock) for the observable in order to subscribe to it.Alarm
Actually your example should just work...hmmmEngen
I guess I'll just test my http calls without any delayAlarm
An observable does not "resolve". It "emits".Tutu
Why setTimeout doesnt work here? Anybody?Lysenkoism
V
204

Using the following imports:

import {Observable} from 'rxjs/Observable';
import 'rxjs/add/observable/of';
import 'rxjs/add/operator/delay';

Try this:

let fakeResponse = [1,2,3];
let delayedObservable = Observable.of(fakeResponse).delay(5000);
delayedObservable.subscribe(data => console.log(data));

UPDATE: RXJS 6

The above solution doesn't really work anymore in newer versions of RXJS (and of angular for example).

So the scenario is that I have an array of items to check with an API with. The API only accepts a single item, and I do not want to kill the API by sending all requests at once. So I need a timed release of items on the Observable stream with a small delay in between.

Use the following imports:

import { from, of } from 'rxjs';
import { delay } from 'rxjs/internal/operators';
import { concatMap } from 'rxjs/internal/operators';

Then use the following code:

const myArray = [1,2,3,4];

from(myArray).pipe(
        concatMap( item => of(item).pipe ( delay( 1000 ) ))
    ).subscribe ( timedItem => {
        console.log(timedItem)
    });

It basically creates a new 'delayed' Observable for every item in your array. There are probably many other ways of doing it, but this worked fine for me, and complies with the 'new' RXJS format.

Viticulture answered 23/2, 2017 at 11:58 Comment(16)
Property 'of' does not exist on type 'typeof Observable'. Do you import your Observable with import {Observable} from 'rxjs/Observable';?Alarm
Yep, I use .of extensively so I guess something strange is up with your rx dependencies..Viticulture
I mean you confirm you're not importing with import {Observable} from 'rxjs'? If yes, I have some investigation to do...Alarm
I am importing as import {Observable} from 'rxjs/Observable'Viticulture
Did you have any success?Viticulture
From this page: npmjs.com/package/rxjs. I deduced I had to explicitly import with import 'rxjs/add/observable/of';. Do you happen to do the same thing? It's still odd though, since it won't chain with .delay(...) and it shows an error when I try rxjs/add/observable/delay...Alarm
I managed to make it work in a non-testing environment with the import: import 'rxjs/add/operator/delay' which adds clarity to the problem. The thing is, in a testing environment, jasmine is not waiting for the delay when calling fixture.detectChanges()Alarm
I have never had those issues I'm afraid, so I can't help you with that..Viticulture
Sure, the testing stuff is kind of off-topic here, but I should be able to handle it now that the way rxjs is imported is clearer for me.Alarm
Understood. I approved your edits for others that have the same issues.Viticulture
What about an observable which throws an error with delay?Macadam
should of(item.pipe ( delay( 1000 ) )) be of(item))).pipe(delay(1000) trying to pipe the array gave me errorsChoate
I believe the updated function is structured incorrectly. @AdrianBer formed the correct answer.Geller
This is what worked for me with rxjs6: from([1, 2, 3, 4, 5, 6, 7]).pipe(concatMap(num => of(num).pipe(delay(1000)))) .subscribe(x => console.log(x));Perversion
@Perversion same, of(item).pipe( delay(1000) ) worked. of(item.pipe... is incorrectPatmos
@Viticulture 's solution worked for me too. Sad that so much code is necessary for such a simple matter...Veator
A
170

In RxJS 5+ you can do it like this

import { Observable } from "rxjs/Observable";
import { of } from "rxjs/observable/of";
import { delay } from "rxjs/operators";

fakeObservable = of('dummy').pipe(delay(5000));

In RxJS 6+

import { of } from "rxjs";
import { delay } from "rxjs/operators";

fakeObservable = of('dummy').pipe(delay(5000));

If you want to delay each emitted value try

from([1, 2, 3]).pipe(concatMap(item => of(item).pipe(delay(1000))));
Apophasis answered 14/6, 2018 at 21:54 Comment(5)
The cleanest solution in my opinion.Coda
This "solution" only works if you emit one item. The delay operator is not invoked for each element in an observable. That is why the horrid concatMap solution is required.Chick
@RickO'Shea, the question is about one emitted value, so that's why this solution.Apophasis
So fresh and so clean !Barrus
I updated my answer for multiple delays @RickO'SheaApophasis
N
19

What you want is a timer:

// RxJS v6+
import { timer } from 'rxjs';

//emit [1, 2, 3] after 1 second.
const source = timer(1000).map(([1, 2, 3]);
//output: [1, 2, 3]
const subscribe = source.subscribe(val => console.log(val));
Nettienetting answered 22/11, 2018 at 3:41 Comment(2)
Good answer, don't forget to unsubscribePacemaker
@Pacemaker this specific instance of timer doesn't need to be unsubscribed (it will auto complete after 1 second). This is because timer isn't using the second argument of interval which would need to have extra code to mark it completed some how.Trihedron
H
8

It's little late to answer ... but just in case may be someone return to this question looking for an answer

'delay' is property(function) of an Observable

fakeObservable = Observable.create(obs => {
  obs.next([1, 2, 3]);
  obs.complete();
}).delay(3000);

This worked for me ...

Hanhana answered 27/4, 2017 at 23:3 Comment(2)
import 'rxjs/add/operator/delay' gives this error now: Module not found: Error: Can't resolve 'rxjs/add/operator/delay'Photosynthesis
Why would you call you observable fake when it's quite real? :)Seneca
H
0

You can use asyncScheduler, it schedules tasks asynchronously, by putting them on the JavaScript event loop queue. It is best used to delay tasks in time or to schedule tasks repeating in intervals.

Link to the documentation

Has answered 19/7, 2022 at 5:50 Comment(0)
K
-2

import * as Rx from 'rxjs/Rx';

We should add the above import to make the blow code to work

Let obs = Rx.Observable
    .interval(1000).take(3);

obs.subscribe(value => console.log('Subscriber: ' + value));
Kaycekaycee answered 11/7, 2018 at 13:34 Comment(1)
import * is a bit of an overkill for this simple taskBroddy

© 2022 - 2024 — McMap. All rights reserved.