Expect for a variety of Observables to finish at different times

I am faced with the challenge of extracting data from an API that is paginated, and unfortunately, I cannot determine the total number of pages in advance. However, I can identify when I have reached the last page. My goal is to develop a function that retrieves each page sequentially.

The structure of the page is as follows:

interface PaginatedResult<T> {
  count: number;
  next: string;
  prev: string;
  results: T[];
}

Once I reach the final page, the next attribute becomes null. My desired approach is demonstrated below:

let data: Data[] = []
let url: string = API_ENDPOINT

while (url !== null) {
  this.http.get(url).subscribe((page: PaginatedResult<Data>) => {
    data = data.concat(page.results)
    url = page.next
  })
}

However, executing this code leads to multiple simultaneous requests until one sets the url variable to null, causing the browser to freeze. I have explored methods for chaining subscriptions but have not discovered a solution to combine an unknown number of subscriptions. With no prior knowledge of the total number of pages, my only option is to retrieve one page at a time.

The main issue lies in needing to wait for each page's data before determining whether the next page should be requested.

Do you have any suggestions or ideas on how to tackle this problem effectively?

It is worth noting that while a similar question on Stack Overflow presents strategies for handling an indeterminate number of observables (link provided), it still differs from my current predicament where both the total number of requests and their sequential execution remain uncertain.

Answer №1

To continuously retrieve new data, you can utilize the expand operator, which will recursively fetch the information until the next attribute in the response becomes null.

this.http.get(url).pipe(
  expand((page) => page.next ? this.http.get(page.next) : EMPTY),
).subscribe((page) => (data = data.concat(page.results)));

Answer №2

Despite the fact that the other solutions function well, they immediately provide access to the data once each request is complete.

If you prefer to hold off until all requests are finished before emitting the value, you can implement a recursive function leveraging the switchMap operator.

const fetchData = (x: PaginatedResult<Data>): Observable<PaginatedResult<Data>> => {
  if (!x.next) {
    return of(x);
  }

  return this.http.get(x.next).pipe(
    map(page => ({...page, results: x.results.concat(page.results) })),
    switchMap(fetchData)
  );
};

this.$data = fetchData({next: 'API_ENDPOINT', results: []} as PaginatedResult<Data>).pipe(map(x => x.results));

Answer №3

Not very familiar with rxjs, but you could try implementing something like this:

let info: Info[] = []
let endpoint: string = API_ENDPOINT

const processInfo = (page: PaginatedResult<Info>) => {
  info = info.concat(page.results)
  if (page.next) {
      this.http.get(page.next).subscribe(processInfo);
  } else {
    // All data retrieved
  }
}

this.http.get(endpoint).subscribe(processInfo);

This method will fetch the information one by one in sequence.

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

Proper method for declaring a global variable within a React component

In the process of working on my react.js project, I have encountered a situation where I need all components to be able to access an object before the main component is rendered. My current approach involves passing the object as a prop to the main compo ...

What is the best method for storing a third-party image in cache?

Running my website, I aim to achieve top-notch performance scores using LightHouse. I have successfully cached all the images I created (Cache-Control: public, max-age=31536000). Unfortunately, third-party website images are not cached. How can I cache t ...

In the world of coding, passing an array by reference may seem

Can you explain why console.log(a) and console.log(b) do not return the same result of [1, 2, 3, 4]? function test(c, d) { c = [1, 2, 3, 4]; d.push(4); } a = [1, 2, 3]; b = [1, 2, 3]; test(a, b); console.log(a); console.log(b); ...

Create a JavaScript function to calculate a 2-tailed t distribution by adapting an already existing method

Can someone help me with implementing a two-tailed t-test in JavaScript? Resource: Student's t distribution in JavaScript for Google Spreadsheet I have taken a potential solution from the link and customized it to function outside of the form: func ...

Unable to track scrolling behavior on Internet Explorer 11

I am facing an issue with my code snippet: this.currentScrollYSub = Observable.fromEvent(window, 'scroll') .throttleTime(5) .subscribe(e => { this.scrollY = window.scrollY; console.log(window.scrollY); // Result: undefined ...

What is the best way to store images in a directory using JavaScript and ASP.NET?

How can I upload and save an image in a folder using ASP.NET, then call and display it? Is it possible to achieve this using AJAX, jQuery, or JavaScript with Web Method? <asp:FileUpload CssClass="image" ID="fileUpload" runat="server" /> I currently ...

Tips on how to retain data from ajax requests when navigating between pages

Background Information In my web application, there is a search page with 3 dropdown boxes that are filled via ajax. The first box contains default locations upon loading the page. When a location is selected, a second dropdown appears with buildings from ...

Utilizing ReactJS and Plyr in tandem for Vimeo API Integration

I'm encountering an issue with my React component that utilizes the https://github.com/selz/plyr media player. Everything works as expected until I unmount the component, triggering an error from the Vimeo API. The specific error is: Uncaught (in prom ...

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 ...

Having trouble with creating SQLite tables using JavaScript within a for loop

I have developed a multi-platform app using AngularJS, JavaScript, Phonegap/Cordova, Monaca, and Onsen UI. In order to enable offline usage of the app, I have integrated an SQLite Database to store various data. After conducting some basic tests, I confir ...

Utilizing jQuery to access Flash functions

When trying to access functions in my SWF using jQuery code, I encounter a compatibility issue with Internet Explorer. The code works fine in all other browsers except for IE. As jQuery is supposed to provide cross-browser functionality, writing addition ...

jQuery.ajax readyState event for HEADERS_RECEIVED

By utilizing the native XMLHttpRequest object, it is feasible to attach an event listener to the onreadystatechange event and receive notifications when the readyState reaches 2, or HEADERS_RECEIVED. This functionality proves valuable as it allows for the ...

Utilizing PHP to dynamically generate href links based on radio button selection in real-time interaction with JavaScript

Currently, I am utilizing the MVC framework within Codeigniter and I have a view that contains a link. It's important to mention that the href value is generated by calling a PHP function which takes 'auth/mylogin' as a parameter: <a hre ...

When printing, the CSS for media print is not correctly displaying the table format

I have a basic table with a button underneath. This code snippet is located in the body section of my JSP file: <div id="myDivForPrint"> <table class="t1"> <tbody> <tr> ...

Interacting between frames with jQuery

I have main_page.htm with the following frameset structure: <frameset rows="30,*" frameborder=0 border=0> <frame name="top_frame" src="top.htm"> <frame name="bottom_frame" src="bottom.htm"> </frameset> The content in ...

Problems encountered while invoking a function using ng-click within an AngularJS controller?

You can check out the code by visiting When attempting to login, I trigger the simple login() function of the AuthController using ng-click on the login form button. However, upon calling this function, I encounter an error message in the console that I a ...

Getting the css property scaleX with JQuery is as simple as executing the

I am struggling to print out the properties of a specific div element using jQuery. My goal is to have the different css properties displayed in an information panel on the screen. Currently, I am facing difficulties trying to show scaleX. Here is my curr ...

The AJAX server call is still experiencing a timeout issue despite having already implemented a timeout

My server ajax call keeps timing out even though the server responds back within a reasonable timeframe. If the server responds within 2-4 minutes, the ajax call goes into success. However, if the server takes longer than 4 minutes to respond, the ajax ca ...

Scroll the div that is dynamically filled without affecting the scrolling of the main page

My current struggle involves using iScroll in my web project. The goal is to populate a list with articles and slide in a div over the list to display the selected article. While the basic functionality is in place, I face an issue where scrolling through ...

How should one approach working with libraries that do not have type definitions in TypeScript?

My current situation involves working with libraries that are untyped and resulting in warnings. I am curious about the best approach to address this issue - should I adjust configurations, use tslint ignore on a line-by-line basis, or possibly create du ...