Building a continuous timer loop in Angular using RxJS that adapts to changing durations within an array's objects

I am experimenting with a scenario where I read the data, loop based on the duration. For example, starting with "Adam" first, play Adam for a 15-second timer, then move on to the next beginner "Andy" and play Andy for 15 seconds.

Once we reach group "intermediate," play "Fam" for 30s, then move to "Jamie" and play for 30s. Repeat for intermediate because the "play" count is 2. Once complete, move to the "expert" group.

let data = [{
    "play": 1,
    "name": "Adam",
    "duration": 15,
    "group": "beginner"
}, {
    "play": 1,
    "group": "beginner",
    "name": "Andy",
    "duration": 15
}, {
    "duration": 30,
    "play": 2,
    "name": "Fam",
    "group": "intermediate"
}, {
    "name": "Jamie",
    "group": "intermediate",
    "duration": 30,
    "play": 2
},
{
    "duration": 45,
    "play": 2,
    "name": "Fam",
    "group": "expert"
}, {
    "name": "Jamie",
    "group": "expert",
    "duration": 45,
    "play": 2
}];
destroy$: Subject<boolean> = new Subject<boolean>();
onStart() {
  from(data).pipe(
    takeUntil(this.destroy$),
    concatMap(record => timer(0, record.duration * 1000).pipe(
    map(i => data[i]))
  ))
  .subscribe(data => {
    this.currentItem = data;
  });
}

ngOnDestroy() {
  this.destroy$.next(true);
  this.destroy$.unsubscribe();
}

Thank you for sharing your idea in advance!

Answer №1

Your code is close to being correct, but there are some nuances when using the timer function with different parameters:

  • timer(0, 15) will emit immediately upon subscription and then every 15ms indefinitely (since it never completes, concatMap will not move on to the next record).
  • timer(15) will emit only once after 15ms and then complete.

To implement this properly, you can use either of the following approaches:

concatMap(record => timer(record.duration * 1000).pipe(
  map(() => record),
),

Alternatively, you could utilize delay():

concatMap(record => of(record).pipe(
  delay(record.duration * 1000),
),

Answer №2

To solve this issue, use the following concatenation map method: concatMap(item => timer(record.duration * 1000).pipe(ignoreElements(), startWith(record)))

Answer №3

Example in action:

import { from, timer, of } from "rxjs";
import { switchMap, repeat, delayWhen, scan, mapTo, concatMap } from "rxjs/operators";

interface Item {
  play: number;
  name: string;
  duration: number,
  group: string;
}

let data: Item[] = [{
    "play": 1,
    "name": "Alex",
    "duration": 15,
    "group": "novice"
}, {
    "play": 1,
    "group": "novice",
    "name": "Bob",
    "duration": 15
}, {
    "duration": 30,
    "play": 2,
    "name": "Cathy",
    "group": "intermediate"
}, {
    "name": "David",
    "group": "intermediate",
    "duration": 30,
    "play": 2
},
{
    "duration": 45,
    "play": 2,
    "name": "Eva",
    "group": "expert"
}, {
    "name": "Frank",
    "group": "expert",
    "duration": 45,
    "play": 2
}];

createSequence(data)
  .subscribe(result => {
    console.log(result);
  });

function createSequence(data: Item[]) {
// apply 'repeat' functionality
  const duplicatedData = data
    .reduce((acc, item) => [ ...acc, ...new Array(item.play).fill(item)], []);
// calculate interval for next item based on previous item
  const dataWithIntervalsAndRepeats = duplicatedData.map(
    (element, index) => ({ 
      element, 
      interval: duplicatedData[index-1] ? duplicatedData[index-1].duration : 0 
    }))

  return from(dataWithIntervalsAndRepeats).pipe(
    concatMap(({element, interval}) => timer(interval * 100)
      .pipe(mapTo(element))
    )
  );
} 

Demonstration available (check browser console): https://stackblitz.com/edit/rxjs-4qu5r6?devtoolsheight=60

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

The sequence of output in TypeScript when using Gulp is similar to running tsc with a tsconfig

After setting up a tsconfig file and successfully running the command-line tsc, I encountered an issue when using gulp-typescript with a tsconfig.json and outFile specified. The output ordering was different, and I have been unable to find a solution in Gu ...

Sometimes the PDF does not display even though the iframe src attribute is updating in Angular 6

I have encountered an issue with displaying a PDF file in my code. Sometimes the PDF shows up correctly, but other times it fails to load. I even tried using an iFrame instead of embed but faced the same scenario. Can anyone provide assistance? Here is my ...

After updating my Angular version from 8 to 9, an error has been thrown stating "It is not possible to assign the value 'undefined' to the template variable 'limit'"

Recently, I made updates to my Angular 8 project by switching it to the newest version of Angular 9. In one of the template's div elements, I declared a variable and everything seemed to be functioning correctly without any errors. To avoid initializi ...

Angular: display many components with a click event

I'm trying to avoid rendering a new component or navigating to a different route, that's not what I want to do. Using a single variable with *ngIf to control component rendering isn't feasible because I can't predict how many variables ...

How to include a sub-route in Angular after adding parameters?

In this scenario: 'www.xyz.com/#/indutry/1/subIndustry/2/subSubIndustry/3' I am looking to implement this structure in my Parent route file. How can I achieve this using ForRoot? ...

Binding two objects to a single event using Angular 2 syntax

Is there a way to connect two simple input fields to a single click event in Angular? One box for typing text and the other providing a timestamp from Date();. How can I show both values when clicking on the button? // The #date input field provides the ...

Refreshing the page causes the Angular/Ionic Singleton instance to be destroyed

I have a TypeScript singleton class that is responsible for storing the login credentials of a user. When I set these credentials on the login page and navigate to the next page using Angular Router.navigate (without passing any parameters), everything wor ...

When utilizing a SSL certificate generated by Let's Encrypt in Node.js, the socket fails to establish a connection

I have set up an https server using a certificate generated with lets encrypt. My goal is to establish a connection between the Socket.io client and the Socket.io server linked to the https server. Unfortunately, the socket keeps disconnecting without any ...

Utilizing conditional types for type narrowing within a function's body: A comprehensive guide

I created a conditional type in my code that constrains the second argument of my class constructor based on the type of the first argument. Although the type checker correctly limits what I can pass to the constructor, I am struggling to get the compiler ...

Property does not exist when dispatching in React Redux within componentDidMount

Currently, I am navigating my way through my initial project using React + Redux and have hit a few roadblocks while attempting to dispatch a function in the componentDidMount section. I tried to emulate the Reddit API example project from the Redux docume ...

Encountering a 403 error when attempting to upload files from Angular to a Micron

I have encountered an issue while attempting to upload multiple files to the Micronaut Rest API. The uploading process works seamlessly with Postman and Swagger in the Micronaut Rest API, but when using the Angular app, the POST method throws a 403 HTTP er ...

React modal not closing when clicking outside the modal in Bootstrap

I recently utilized a react-bootstrap modal to display notifications in my React project. While the modal functions correctly, I encountered an issue where it would not close when clicking outside of the modal. Here is the code for the modal: import Reac ...

Ways to display an icon with an underline effect upon hovering over text

I have implemented a CSS effect where hovering over text creates an underline that expands and contracts in size. However, I now want to display an icon alongside the text that appears and disappears along with the underline effect. When I try using displa ...

Performing an insertion in TypeORM with a foreign key connection

In my database schema, I have set up a relationship where each Chatroom can have multiple Messages linked to it. However, when I try to insert a Message (or a batch of Messages), the foreign key for ChatRoom is not being assigned properly and remains null. ...

Missing data: null or undefined?

When using vuex-module-decorators, is it better to set the default value of a data property to null or undefined? For example: export default class ComponentName extends Vue { post: BlogPost | null = null } or export default class ComponentName extends V ...

Can the TypeScript Event class be customized and extended?

Snippet of Code: class CustomEvent extends Event { constructor(name) { super(name); } } var customEvent = new CustomEvent("scroll"); Error Encountered During Runtime: An error occurred: Uncaught TypeError: Failed to construct 'Ev ...

Finding the number enclosed within two characters - TypeScript

Recently, I've started exploring Typescript and I'm currently working on creating a webhook within my Google Cloud Functions. In one of the scenarios, I have this string: C1234567890A460450P10TS1596575969702 My objective is to utilize regex to ...

AngularJS 2 TypeScript structure

My application includes a user service for managing user operations and an interface for the user object. user.service.ts import {Injectable} from 'angular2/core'; export interface User { name: string; email?: string; picture?: string; } ...

Determining Refresh Status in Angular Application

Does Angular provide a method to determine if the browser has been refreshed? I need to verify whether the page has been refreshed or not, as I want to run a function only when the value is false. In my ngOnInit function, I have the following code: pageIs ...

Challenges with Displaying Filtered Arrays in Angular

Currently, I am working through a tutorial and have hit a snag in a particular feature. The task at hand is to display a filtered array (which comes from firebase) when clicking on an anchor. Despite following all the necessary implementation steps, I seem ...