Operators within an observable that perform actions after a specific duration has elapsed

Is there a way in an rxjs observable chain to perform a task with access to the current value of the observable after a specific time interval has elapsed? I'm essentially looking for a functionality akin to the tap operator, but one that triggers only if a certain amount of time passes without any new values emitted by the observable. In essence, it's like a combination of tap and timeout.

Here's a hypothetical scenario:

observable$.pipe(
  first(x => x > 5),
  tapAfterTime(2000, x => console.log(x)),
  map(x => x + 1)
).subscribe(...);

This example is fabricated, and the "tapAfterTime" function doesn't actually exist. However, the concept revolves around the idea that if 2000ms go by after subscribing to the observable without encountering a value greater than 5, then execute the tapAfterTime callback function on the current value of the observable. If a value greater than 5 is received before the 2000ms mark, the tapAfterTime callback won't trigger, but the map function will proceed as planned.

Does anyone know of an operator or combination of operators that could achieve this behavior?

Answer №1

Perhaps this concept is a bit complex, but it may be worth exploring.

The approach involves creating two distinct observables by transforming the source observable$ and then merging them together.

The first Observable, referred to as obsFilterAndMapped, is where filtering and mapping occur.

The second Observable, known as obsTapDelay, triggers a new timer with a specified delay each time the first Observable (obsFilterAndMapped) emits a value. If the delayTime is exceeded before a new value is emitted, the tapAfterTime action is executed. Otherwise, a new timer is set for the next emissions.

Here is the code implementing this concept:

const stop = new Subject<any>();
const obsShared = observable$.pipe(
    finalize(() => {
        console.log('STOP');
        stop.next();
        stop.complete()
    }),
    share()
);
const delayTime = 300;
const tapAfterTime = (value) => {
    console.log('tap with delay', value)
}; 

let valueEmitted;

const obsFilterAndMapped = obsShared.pipe(
    tap(val => valueEmitted = val),
    filter(i => i > 7),
    map(val => val + ' mapped')
);

const startTimer = merge(of('START'), obsFilterAndMapped);

const obsTapDelay = startTimer.pipe(
    switchMap(val => timer(delayTime).pipe(
        tap(() => tapAfterTime(valueEmitted)),
        switchMap(() => empty()),
    )),
    takeUntil(stop),
)

merge(obsFilterAndMapped, obsTapDelay)
.subscribe(console.log, null, () => console.log('completed'))

By using this method, you can perform the tapAfterTime action whenever the source observable$ does not emit any value for longer than the delayTime duration. This functionality applies throughout the lifecycle of observable$.

You can test the code with the following input:

const obs1 = interval(100).pipe(
    take(10),
);
const obs2 = timer(2000, 100).pipe(
    take(10),
    map(val => val + 200),
);
const observable$ = merge(obs1, obs2);

Further improvements could involve encapsulating the global variable valueEmitted within a closure, although this may add complexity to the code without significant benefits.

Answer №3

Here's an example of how it could look:

let timeout;
observable$.pipe(
  tap((x)=>timeout=setTimeout(()=>console.log(x), 2000)),
   filter(x => x > 5),
   tap(x => clearTimeout(timeout)),
   map(x => x + 1)
);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Encountering a connection issue while deploying an Angular Node application on AWS EC2: "Server connection could not be established."

I am currently facing an issue while trying to deploy my Angular/Node application on AWS EC2. Despite multiple attempts, I cannot seem to resolve this error. https://i.sstatic.net/OCUqe.png The app was constructed using the command ng build --prod This ...

What do you call a JavaScript function when it has a name

This code is confusing to me. It's not the usual JavaScript syntax for a function that I know of. Is this a specific function? Or perhaps it's a callback for when an update event occurs? Apologies for these beginner questions, as I am quite new t ...

Encountering Angular Material UI issues following the transition from version 11 to 12

I am currently in the process of updating my Angular application from version 11 to 12, integrating Angular Material, and encountering some error messages: Error No.1 - Error NG8002: Cannot bind to 'ngModel' as it is not a recognized property of ...

What is the process of transferring JavaScript code to an HTML file using webpack?

I am looking to display an HTML file with embedded CSS, fonts, and JS (not linked but the content is inside). I have the CSS and fonts sorted out, but I am struggling to find a solution for the JavaScript. My project is based on Node.js. ...

Directive encountered an error and could not be comprehended

Having an issue with an Angular5 directive I created in my app that is throwing an error. I'm struggling to pinpoint the problem, can anyone offer some assistance? Here's the code: import { Directive, ElementRef, HostListener, Input, Renderer } ...

What is the purpose of using $ symbols within NodeJS?

Lately, I've been attempting to grasp the ins and outs of using/installing NodeJS. Unfortunately, I'm feeling a bit lost due to tutorials like the one found here and their utilization of the mysterious $ symbol. Take for instance where it suggest ...

When emitting an event multiple times in Angular, an error may occur where properties of undefined are unable to be read, particularly in relation to the "

I am encountering an issue with my event binding on a button, specifically (click)="onStart()". The problem arises when the event this.numEmitter is emitted for the first time in setInterval, after which I receive the error message ERROR TypeError: Cannot ...

The toggle feature in Bootstrap is stuck in the "on" position and refuses to "untoggle."

Currently, I am working on developing a basic website using Laravel and Vue.js. To ensure that the website is responsive, I integrated Bootstrap 4 into the project. For the most part, everything appears to be functioning correctly - the navbar collapses as ...

Having trouble with my Angular subscription - not behaving as I anticipated

I am facing an issue on my shop page where I have a FilterBarComponent as a child component. On initialization, I want it to emit all the categories so that all products are rendered by default. However, on my HomePageComponent, there is a button that allo ...

Is there a library available for generating QR codes on the server side and saving them directly to a database

My Goal: I am looking to create a functionality where, upon clicking "Generate QRCode," JavaScript will utilize the local machine's datetime to generate an md5 hash in the MMDDYYHHMMSS format. I then want this hash to be sent to the server to produce ...

Leveraging RXJS for real-time response to keyboard and mouse click combinations in web

I am new to RXJS and looking for a way to drag an HtmlElement when the user presses the space key and then drags the element with the mouse. The dragging should be initiated by either pressing the SPACE key followed by a left click, or vice versa. The dra ...

Getting a variable from an ajax call and using it in a Pug template

Currently, I am experimenting with Express and Pug to learn some new concepts. Upon making a request to a website, I received some dynamic information stored in an array. However, I am facing challenges when trying to iterate over this array using Pug. The ...

Dynamic Styling in Angular 2/4: A Modern Approach

Can someone help me with Angular 2/4 syntax for this code snippet? $('nav ul li a').click(() => { $(this).closest('li').addClass('active'); }); ...

What is the best way to create a universal limitation for a larger collection of a discriminated union?

Is it possible to enforce that when defining a generic class Foo<X>, where X represents a discriminated union type, X must be a superset of another discriminated union Y? In my specific scenario, I am utilizing a discriminated union to differentiate ...

The request returned a 404 (Not Found) error message when trying to navigate using AngularJS

Currently, I am working on building a straightforward application using Ionic and Angular. To test my progress locally, I have set up a simple server by running Ionics ionic serve command. Below is the snippet of my playlist.html code, where I intend to s ...

Learn the dynamic binding method for <a href> templates in Angular 2!

Kindly review the code snippet provided below: Component.ts getReply():string{ if(condition){ return "The link is: <a href = 'https://www.google.com'>Google</a>"; } } In my front end, I am using [innerHtml] to bind ...

AngularJS is incapable of detaching forms from objects

Creating forms dynamically using ng-repeat and adding them to the vm.forms object is causing issues. When an item is removed from the array, the corresponding form is deleted but the key in the vm.forms object remains, leading to undefined errors. angul ...

Customize the asset path location in Webpack

I am currently working on a Vue application that utilizes Webpack for code minimization. Is there a method to dissect the chunks and adjust the base path from which webpack retrieves the assets? For example, by default webpack fetches assets from "/js" o ...

I have a query regarding the process of filtering data, specifically in the context of

When working with express and mongoose, I often encounter complex queries. As a workaround, I typically retrieve objects by their ID like this: const ticketObj = await Ticket.findById(ticketId); I then use JavaScript's filter method to further narro ...

Using Vue.js to pass a variable from a parent component to a child component

Parent component: ShowComment Child component: EditComment I'm attempting to pass the value stored in this.CommentRecID to the child component. In the template of ShowComment, I have included: <EditComment CommentRecID="this.CommentRecID" v-if= ...