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

Having trouble with Grunt and Autoprefixer integration not functioning properly

Joining a non-profit open source project, I wanted to contribute by helping out, but I'm struggling with Grunt configuration. Despite my research, I can't seem to figure out why it's not working. I am trying to integrate a plugin that allow ...

Select the five previous siblings in reverse order using jQuery's slice method

I have a unique approach to displaying a series of divs where only 5 are shown at a time using the slice() method. To navigate through the items, I include an ellipsis (...) link after every 4th item, which allows users to easily move on to the next set of ...

How can I merge these two Observables in Angular to create an array of objects?

Let's say we are working with two different datasets: student$ = from([ {id: 1, name: "Alex"}, {id: 2, name: "Marry"}, ]) address$ = from([ {id: 1, location: "Chicago", sid: 1}, {id: 2, location: &qu ...

Drop and drag the spotlight

On my website, I am looking to implement a feature that will make it easier for users to identify the drag and drop area. I found a code snippet on JSFIDDLE that works perfectly there. However, when I tried to use it on my local server, it doesn't se ...

Guide on transferring JSON information from a client to a node.js server

Below is the code snippet from server.js var express = require("express"), http = require("http"), mongoose = require( "mongoose" ), app = express(); app.use(express.static(__dirname + "/client")); app.use(express.urlencoded()); mongoose.con ...

Encountering a 404 error when attempting to post from a Node.js express app to a

Trying to post to a MySQL database has been giving me a 404 error. I have searched through various posts here, but none of the accepted solutions seem to work for me. I'm struggling to figure out what I am doing wrong. When utilizing a GET request, t ...

Adding an image to a React component in your project

I am currently working on an app that utilizes React and Typescript. To retrieve data, I am integrating a free API. My goal is to incorporate a default image for objects that lack images. Here is the project structure: https://i.stack.imgur.com/xfIYD.pn ...

What are the implications of a project containing nested node_modules directories?

We are currently in the process of dividing our project into "sub modules" within a single repository. Our goal is to maintain aspects such as webpack configurations and express server globally, with a structure similar to the following: package.json serv ...

Loading a specific number of rows in Datatables upon page loading: How to do it?

Recently, I came across a code snippet that uses AJAX to retrieve data from a specific URL and then displays the information in a table. However, I have a requirement to only load and display the first 10 rows of data during the initial page load process. ...

What is the best way to achieve varying margins when adding divs in CSS?

Encountering CSS margin issues when adding new divs. Desiring a large margin between the Create Div button and minimal margin between Example Text Here. The desired outcome Margin is too small between Create Div button and Example Text Here, but good bet ...

Creating a search functionality in Angular that allows users to input multiple search terms and

I am currently delving into the world of Angular in combination with an API, and I have managed to set up a search box for querying data. However, I am facing a challenge where I cannot perform multiple searches successfully. Even though I can initially se ...

Experiencing difficulties loading Expo Vector Icons in Nextjs

I've spent countless hours trying various methods to accomplish this task, but unfortunately, I have had no luck so far. My goal is to utilize the Solito Nativebase Universal Typescript repository for this purpose: https://github.com/GeekyAnts/nativ ...

Customized input field design for a discussion board platform

I am in the process of creating a forum-style website and I am in search of a unique open source HTML template that includes an editable text box where users can input their posts, similar to what is seen when posting a question or answer. I believe there ...

Is there a way to use JavaScript to switch the entire div on and off

I have a function called done that I want to use to toggle the visibility of my "temp" division. tasks.innerHTML += `<div id="temp"> <span id="taskname"> ${input.value} </span> <button class="d ...

Unable to pass data from a Jquery ajax request to another function

I've written a basic ajax request using jQuery. Here is the code for my ajax function: var sendJqueryAjaxRequest = function(arrParams) { var request = $.ajax({ url: arrParams['url'], async: false, ...

What is the method in JavaScript for a child function to trigger a Return statement in its parent function?

I have encountered a unique problem. I need to retrieve some data downloaded via ajax and return it, but neither async nor sync modes are fetching the data in time for the return. Is there a way to call the return from a child function to the parent func ...

The requested resource does not have the 'Access-Control-Allow-Origin' header

Currently, I am working on an application that utilizes Angular for the client side and NodeJs for the backend. The application is being hosted with iis and iisnode. Recently, I implemented windows authentication to the application in order to track which ...

Unable to finish the execution of the ionic capacitor add android command

My current project needs to add android as a supported platform, so I tried running the command: ionic capacitor add android. However, when I run the command, it stops at a prompt asking me "which npm client would you like to use? (use arrow keys)", with ...

Guide to dynamically displaying different URLs based on checkbox selection using Ajax

My goal is to dynamically change the view of a page based on which checkbox is checked. Additionally, I want to ensure that when one checkbox is selected, another becomes unchecked. <input class="searchType" type="checkbox"></input> <input ...

The ng2-image-viewer does not support the newest versions of Angular (11 and above)

Encountering an issue with ng serve: Error message: ERROR in node_modules/ng2-image-viewer/index.d.ts:3:22 - error NG6003: Appears in the NgModule.exports of SharedModule, but could not be resolved to a NgModule, Component, Directive, or Pipe class. This ...