Switching between API requests through a live feed

Hey there:

import Rx from 'rxjs';

function mockApi(endpoint, time, response) {
  return new Rx.Observable(observer => {
    console.log(`${endpoint}: Request initiated.`)
    let active = true;
    const id = setTimeout(() => {
      console.log(`${endpoint}: Response received.`)
      active = false;
      observer.next(response);
      observer.complete();
    }, time);
    return () => {
      if(active) console.log(`${endpoint}: Cancel operation.`)
      clearTimeout(id);
    }
  })
}

function fetchFromApi() { return mockApi('Search', 4000, "This is a result of the search."); }


//============================================================

const messages$ = new Rx.Subject();

const trigger$ = messages$.filter(m => m === 'toggle');

const completedSearch$ = trigger$.flatMap(() => 
  fetchFromApi().takeUntil(trigger$)
);

completedSearch$.subscribe(m => console.log('Subscriber:', m))

setTimeout(() => {
  // Initiates API call.
  trigger$.next('toggle'); 
}, 2000)

setTimeout(() => {
  // Cancels ongoing API call without starting a new one.
  trigger$.next('toggle'); 
}, 3000)

setTimeout(() => {
  // Starts a new request.
  trigger$.next('toggle'); 
}, 9000)

I aim to initiate and terminate an API call using the same trigger$ signal. The problem lies in how the code currently functions - it triggers a new API call each time. I want it to only halt an existing call when in progress, not commence a fresh one. Perhaps there's a need to detach the outermost flatMap subscription from the trigger$ stream while fetchFromApi() runs. Any suggestions on restructuring the code for this desired behavior? What's the ideal RxJS approach here?

UPDATE: After further exploration and referencing user guides, I arrived at this solution:

const completedSearch$ = trigger$.take(1).flatMap(() =>
  fetchFromApi().takeUntil(trigger$)
).repeat()

Operates as intended. Still seems slightly enigmatic. Is this the typical RxJS methodology to resolve such scenarios?

Answer №1

It seems like the solution provided will only work on a single occasion due to the usage of take(1). An alternative approach could be:

const searchDone$ = toggle$
    .let(observable => {
        let pending;

        return observable
            .switchMap(() => {
                let innerObs;

                if (pending) {
                    innerObs = Observable.empty();
                } else {
                    pending = innerObs = apiSearch();
                }

                return innerObs.finally(() => pending = null);
            });
    });

The use of let() is limited to encapsulating pending without defining it in the parent scope. The switchMap() operator automatically unsubscribes without requiring take*().

When applying your test with setTimeout, the expected output will be as follows:

Search: Request.
Search: Cancel.
Search: Request.
Search: Response.
Subscriber: This is a result of the search.

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

Ensure that the link's color remains constant and remove any styling of text-decoration

Attempting to create a customized header. My goal is to change the color and remove text decoration in the navbar. Below is my code snippet: ReactJS: import React from 'react'; import './Header.css'; function Header() { return ( ...

Can someone explain the process of unescaping characters in an API response to me?

I have created an application that leverages AngularJS to pull data from the WP Rest API V2. The response includes escaped characters, like the example below: "excerpt": { "rendered": "<p>When we go shopping, we encounter many different labeling ...

"Regarding compatibility with different browsers - IE8, Firefox3.6, and Chrome: An inquiry on

Snippet of JavaScript Code: var httprequest = new XMLHttpRequest(); var time = new Date(); if (httprequest) { httprequest.onreadystatechange = function () { if (httprequest.readyState == 4) { alert("OK"); } }; httprequest.open("GET", ...

Maintaining the Readability of Promise Chains

Creating promise chains with arrays is a method I've become accustomed to. It's quite simple to follow along a chain of promises when each one fits neatly on its own line like so: myArray.map(x => convertX) .filter() .whatever() .etc() ...

Navigate a first person simulation using three.js and control your movements with the keyboard arrow

To access my reference material, please go to http://jsfiddle.net/fYtwf/ Overview I am working on a basic 3D simulation using three.js where the camera is surrounded by cubes in three dimensions. These cubes serve as a visual guide to where the camera is ...

Adding supplementary documents within the app.asar file via electron

This is a Vue application using electron-builder. { "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build", "electron:build": "vue-cli-service electron:build", "electron:serve": "vue-cli-service electron:serve", ...

Send the ID of the checkbox to a PHP file using AJAX

Is it possible to generate a network graph by selecting checkboxes? When I choose one or more checkboxes and click the button, I expect to see a network graph with the selected checkboxes. It almost works, but when I select multiple checkboxes, only one ...

Updating input text value using jQuery - Chrome extension

I have been attempting to modify the input value on a specific website using a Chrome extension. To achieve this, I am utilizing jQuery in my content script. While it is effective in most scenarios, I encountered difficulty changing the input value when ...

Encountering difficulty when integrating external JavaScript libraries into Angular 5

Currently, I am integrating the community js library version of jsplumb with my Angular 5 application (Angular CLI: 1.6.1). Upon my initial build without any modifications to tsconfig.json, I encountered the following error: ERROR in src/app/jsplumb/jspl ...

Is there a way to utilize JSON data to dynamically fill column names in a JQuery datatable?

JSON Data: { "data": [ { "name": "Garrett Winters", "designation": "Accountant", "salary": "$170,750", }, { "name": "Brielle Williamson", "designation": "Integration Specialist", "salary": "$372,000", } ] } H ...

Change the WordPress Divi Builder nav bar to switch from transparent to white when scrolling or upon reaching a specific point on the page

Currently, I am in the process of revamping our company's WordPress website using the Divi Builder. For the past couple of days, I have been scouring the internet trying to find a solution that would allow the navigation bar to change CSS classes as t ...

Display chart labels are constantly visible in Vue Chart JS

In my Vue.js JavaScript application, I am working on setting up a chart where I want the labels and chart information to be visible at all times without requiring mouse hover. Check out this image of the chart. This is the code snippet for my chart: < ...

Is it possible to repeat this action by swiping to the left?

I'm currently developing an app in PhoneGap and retrieving information using JSON. My goal is to trigger this function again with Ajax when I slide left. This is the code I have so far. Thank you to everyone for your assistance. $(document).ready( ...

How can I retrieve all links using XPath in Puppeteer when there are issues with pausing or functionality?

I am having issues with using XPaths to select all links on a page in my Puppeteer app. Sometimes the method I am using gets stuck and causes my crawler to pause. I'm wondering if there is a better way to retrieve all links from an XPath or if there m ...

Changing the prototype of a generator function

TL;DR I am looking to enhance a generator function instance by adjusting its prototype, specifically the object received when calling a function*. Imagine I have a generator function: function* thing(n){ while(--n>=0) yield n; } Next, I create a ...

What is the process for incorporating beforeAnimate and afterAnimate callbacks into a custom jQuery plugin?

Let's imagine I have developed a plugin: // creating the plugin (function($){ $.fn.myPlugIn = function(options){ defaults = { beforeAnimate : function(){}, afterAnimate : function(){} ...

Enhancing the $routeProvider templateUrl request with extra data

I am working on adding headers to my request for fetching templates to filter out non-AngularJS requests and return an error message for them. It seems like the only way to achieve this is by using $http, but I feel like it would be a waste to recreate a ...

acquire information from a variable using angularjs

Given the following variable: var exampleVar = {"id": 0, "Nodeid": 1234, "title":"abc"}; I am looking to retrieve the Nodeid value from the above and save it in a new variable. The desired output should be: var newNodeID = 1234; ...

Using AngularJS to update text content retrieved from a file and display it in a textarea

Is there a way to create a textarea using AngularJS that can be populated either by typing, entering a URL, or uploading a plain text file? While I am able to load the file content into the variable linked to the textarea, I'm facing an issue where ge ...

Tips for identifying if the cursor is hovering over the :before or :after section of an element

One of the challenges I am facing involves CSS and defining drop areas for users to interact with, allowing them to either drop a section before or after existing sections. .section:before, .section:after { content: "[insert here]"; height: 64px; ...