What steps can I take to guarantee that the observer receives the latest value immediately upon subscribing?

In my Angular 2 and Typescript project, I am utilizing rxjs. The goal is to share a common web-resource (referred to as a "project" in the app) among multiple components. To achieve this, I implemented a service that provides an observable to be shared by all clients:

/**
 * Provided to clients for subscription.
 */
private _observable : Observable<Project>;

/**
 * Used for emitting events to clients.
 */
private _observer : Observer<Project>;

constructor(private _http: Http) {
    // Create the observable and observer once to be shared with all subscribers.
    this._observable = Observable.create( (obs : Observer<Project>) => {
        this._observer = obs;
    });
}

Clients can simply reference the _observable and subscribe to it.

/**
 * Returns an observable always pointing to the active project.
 */
get ActiveProject() : Observable<Project> {
    return (this._observable);
}

When a component wants to load a specific project, it triggers the following method:

/**
 * @param id The id of the project to set for all subscribers
 */
setActiveProject(id : string) {
    // Prevents project changes during ongoing requests
    if (this._httpRequest) {
        throw { "err" : "HTTP request in progress" };
    }

    this._httpRequest = this._http.get('/api/project/' + id)
        .catch(this.handleError)
        .map(res => new Project(res.json()));

    this._httpRequest.subscribe(res => {
        // Cache the project
        this._cachedProject = res;
        // Indicates no more requests are pending
        this._httpRequest = null;
        // Notifies subscribers about the change
        this._observer.next(this._cachedProject)

        console.log("Retrieved project");
    });
}

This code performs an HTTP request, transforms the JSON data into an instance, and uses this._observer.next() to inform all subscribers about the update.

If a subscription occurs after the initial HTTP request, the subscriber won't receive any data until a new request is sent. I've learned about a caching or replay mechanism in rxjs that addresses this issue, but I'm unsure how to implement it.

tl;dr: How can I ensure that a call to subscribe on the observer initially receives the most recent value?

Extra question: Did removing the observer from the observable in the constructor essentially create a subject?

Answer №1

So, this is how the BehaviorSubject works in action.

import { BehaviorSubject } from 'rxjs/subject/BehaviorSubject';
...
obs = new BehaviorSubject(4);
obs.subscribe(); // outputs 4
obs.next(3); // outputs 3
obs.subscribe(); // outputs 3

Answer №2

I typically use shareReplay(1) to achieve this functionality. When using this operator with a parameter of 1, the latest emitted value is stored in a buffer so that it can be immediately passed on to any new subscriber. For more information, refer to the documentation.

var interval = Rx.Observable.interval(1000);

var source = interval
    .take(4)
    .doAction(function (x) {
        console.log('Side effect');
    });

var published = source
    .shareReplay(3);

published.subscribe(createObserver('SourceA'));
published.subscribe(createObserver('SourceB'));

// Creating a third subscription after the previous two subscriptions have
// completed. Notice that no side effects result from this subscription,
// because the notifications are cached and replayed.
Rx.Observable
    .return(true)
    .delay(6000)
    .flatMap(published)
    .subscribe(createObserver('SourceC'));

function createObserver(tag) {
    return Rx.Observer.create(
        function (x) {
            console.log('Next: ' + tag + x);
        },
        function (err) {
            console.log('Error: ' + err);
        },
        function () {
            console.log('Completed');
        });
}

// => Side effect
// => Next: SourceA0
// => Next: SourceB0
// => Side effect
// => Next: SourceA1
// => Next: SourceB1
// => Side effect
// => Next: SourceA2
// => Next: SourceB2
// => Side effect
// => Next: SourceA3
// => Next: SourceB3
// => Completed
// => Completed
// => Next: SourceC1
// => Next: SourceC2
// => Next: SourceC3
// => Completed

Extra question: By "pulling the observer out of the observable" (in the constructor), have I essentially created a subject?

I am not sure what you mean by that, but no. A subject is both an observer and an observable and have specific semantics. It is not enough to 'pull the observer out of the observable' as you say. For subjects semantics, have a look here : What are the semantics of different RxJS subjects?

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

Issue encountered while implementing async functionality in AngularFireObject

I'm encountering difficulties with the async feature in AngularFireObject. Is there a solution available? Snippet from HomePage.ts: import { AngularFireObject } from 'angularfire2/database'; export class HomePage { profileData: Angu ...

Is it possible for me to introduce an additional variable to the String.prototype object?

I have a question that has been bugging me out of curiosity. I was thinking about whether I can add an additional variable in front of String.prototype. For instance: $.String.prototype.functionName = function(){}; Obviously, this doesn't work as i ...

Error: Trying to modify a property that is set as read-only while attempting to override the toString() function

I have a specific object that includes an instance variable holding a collection of other objects. Right now, my goal is to enhance this list of elements by adding a customized toString() method (which each Element already possesses). I experimented with t ...

Unraveling the mystery: accessing the same variable name within a function in JavaScript

What is the best way to reference the same variable name inside a function in JavaScript? var example = "Hello"; console.log("outside function: " + example) function modifyVariable() { var example = "World!"; console.log("inside function: " + ex ...

Steps to establish the starting value as 'Concealed'

I stumbled upon this interesting example that demonstrates how to create expandable/collapsible text when clicked. My question is, how can I initially set the value to be hidden so that the paragraph starts off collapsed and expands only when clicked? He ...

Is this filter selector in jQuery correct?

It appears to function correctly, but I am unsure if there is room for improvement. I aim to target any HTML element with a class of edit-text-NUM or edit-html-NUM and adjust its color. This is the code snippet I am currently utilizing... jQuery(document ...

How to properly read a multipartform-data stream in NodeJS

I am attempting to handle a multipartform-data stream that may consist of various files and fields, in order to save the files to a directory on a uWebsockets.js server. Below is the code I am using: let boundary = null; let fields = []; let st ...

The query parameter is not defined in the router of my Next.js app API

I'm currently working on building an API endpoint for making DELETE requests to remove albums from a user's document in the MongoDB Atlas database. Struggling with an error that keeps popping up, indicating that the albumName property is undefin ...

What is the method for setting the content-type in an AJAX request for Android browsers?

I am facing an issue with my ajax call to the Rails server. The response from the server varies between HTML and JSON based on the content type. Surprisingly, this works smoothly on iPhone and desktop browsers like Chrome, but I am encountering a problem o ...

AWS Lambda serverless deployment of Angular Universal is failing to detect javascript files in the dist/browser directory

After following the steps in this tutorial for deploying to a lambda function, I encountered some issues. When testing it using serverless offline, I kept getting 404 errors for each compiled JS file. However, once I deployed it, the errors changed to 403. ...

Encountered an issue while using OpenAPI 3.1 with openapi-generator-cli typescript-fetch. Error: JsonParseException - The token 'openapi' was not recognized, expected JSON String

I am interested in creating a TypeScript-fetch client using openapi-generator-cli. The specifications were produced by Stoplight following the OpenAPI 3.1 format. However, when I execute the command openapi-generator-cli generate -i resources/openapi/Attri ...

Node.js not resetting array properly

I have successfully set up a Node+Express API that is working smoothly, but I am facing an issue with returning responses for complex queries. The problem lies in the fact that the variable where I store the response data is not being reset between differe ...

Showing a DIV multiple times with an additional text field in HTML and JS

As someone new to development, I am facing a requirement where I need to create a form with a dynamic field that can be added or removed using buttons. When the user clicks on the Add button, another field should appear, and when they click on Remove, the ...

What is the reason the server is not receiving the cookie?

I am currently running a nodejs express application on a server with two endpoints: POST /session - used to send back the cookie GET /resource - used to check if the cookie is sent back, returning 401 not found if it's not On the frontend, hosted on ...

Juicer- Setting restrictions on the frequency of posts

How can I limit the number of posts displayed using the react-juicer-feed component? import { Feed } from 'react-juicer-feed'; const MyFeed = () => { return ( <Feed feedId="<feed-id>" perPage={3} /> ...

Using HapiJs in Node.js to communicate data back and forth with the client

I have client.js and server.js files that are used to send data to my server using ajax. I am able to successfully send the searched username, but on the server side, the domain is being received as undefined. I'm unsure if the issue lies on the clien ...

Receiving the error "Undefined chart type" when using $q.all with Google Chart API Promise

I am currently working on implementing angular-google-charts using the $http service to retrieve data and display it on charts. I found a helpful tutorial on this blog post: since the GitHub README for angular-google-charts lacks examples of backend data ...

Having trouble displaying information in a table using React JS

I devised a feature to display one column of a table and one column for checkboxes (each row should have a checkbox). I stored this file in a component folder with the intention of creating a page where the user selects an account type, and then a new tabl ...

How to use AngularJS to collapse various panels with unique content

Hey everyone, I'm working on developing a collapsible panel using Angular. The panel should consist of a header and body to display the content. The desired behavior is that when a button is clicked, the content collapses down, and clicking the same b ...

Guide to sending a post request with parameters in nuxt.js

I am trying to fetch data using the fetch method in Nuxt/Axios to send a post request and retrieve specific category information: async fetch() { const res = await this.$axios.post( `https://example.com/art-admin/public/api/get_single_cat_data_an ...