convert a function into rxjs observable - rxjs

Imaging I have this code
function sayHi(){
console.log("hi")
}
sayHi();
sayHi();
sayHi();
sayHi();
sayHi();
Above code will log hi 5 times
But I want to use debounce operator and log it only once in every 1 sec.
How do I achieve that?

what about internal function: e.g.
import { interval } from 'rxjs';
const source = interval(1000).subscribe(val => console.log('hi'));
link to docs
----- Edited -----
Lets try something else:
hi$ will be Subject<void> and we will be subscribe to him to console something
hi$.pipe(...).subscribe(() => {
console.log('hi');
});
Let's define some function for calling hi$ subject. this can be called any time in any amount.
let fn = () => {
hi$.next();
// ...
}
Last think is filter console.log to force it to run only once per second. For this i recommend throttle operator, debounce stop calling subscriber for some time, so:
hi$.pipe(throttle(1000)).subscribe(() => {
console.log('hi');
});

Related

Angular 9 and rxjs - wait for message event after postMessage

I am new to rxjs and not sure how to implement the follow logic. Any suggestion will be appreciated.
Background
I am going to implement the communication between host website and an iframe in it with postMessage. Since postMessage is one-way only, I would like to implement the logic to wait for 'response' by myself when a message is sent from host website to iframe.
I have a sync function called send(message) to invoke the postMessage to send message to iframe. Then I would like to have another function with the follow logic.
public async sendAndWait(message): Promise<responseObj> {
// 1. create an observable to wait to message event with timeout
// my first thought is as follow but I feel like it does not work
// fromEvent(window, 'message')
// .pipe(timeout(timeoutInMs))
// .subscribe(event => {
// console.info(event);
// });
// 2. run `send(message)` function
// 3. do not finish this function until timeout or receive event in the previous subscription.
}
When I use the function, I would like to have
let response = await sendAndWait(message);
Not sure if it is possible to implement? Thank you
You cannot stop code execution in JS (using Async-Await, a Promise object is returned behind the scenes. so that the code is never waiting)
Consider implementing it in the following way:
let response: responseObj;
function main(): void {
sendAndWait(MESSAGE_OBJECT).subscribe(x => response = x)
}
function sendAndWait(message): Observable<responseObj> {
send(message)
return fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first()
)
}
Or optionally returning Promise:
async function sendAndWait(message): Promise<void> {
send(message)
const response = await fromEvent(window, 'message')
.pipe(
timeout(timeoutInMs),
first(),
toPromise()
)
}

Observable - set constant delay between one poll return and another poll start

I want to create an Observable-based poller that waits a certain amount of time between the previous request returning and the next request going out.
Here's the code I tried, but this sets a delay between requests going out:
import {timer} from "rxjs";
this.timer = timer(1, POLLING_INTERVAL)
.pipe(concatMap(
(_) => getData()
)).subscribe((data) => {
// do something with data
});
timer isn't ideal for this. Rather use repeatWhen with delay.
import { of } from 'rxjs';
import { repeatWhen, delay } from 'rxjs/operators';
getData().pipe(
repeatWhen(notifications => notifications.pipe(
delay(POLLING_INTERVAL),
)),
).subscribe(...);
Live demo: https://stackblitz.com/edit/rxjs-2evzzi
You has to use the creation interval for:
https://stackblitz.com/edit/typescript-ohddud?file=index.ts&devtoolsheight=100
Or timer with two parameters:
https://stackblitz.com/edit/typescript-h9pzxr?file=index.ts&devtoolsheight=100
I hope you merge request in a correct way.

Rxjs refCount callback to cleanup once every subscribers have unsubscribed?

I've got an observable which is not long lived (http request).
I'm using publishReplay(1) and refCount() so that when there an attempt to access it at the same time, it'll return the same value without triggering the http call again.
But if all the subscriptions are unsubscribed, I need to make some cleanup.
I can't use finalize because:
if I use it before publishReplay then it get closed once the http request is done
if I use it after refCount it'll be run as soon as one observable unsubscribe (instead of when all have unsubscribed)
So basically what I'd like would be to pass a callback to refCount and call that callback when the number of subscriptions reaches 0. But it doesn't work like that. Is there any way to be "warned" when all the subscribers have unsubscribed?
The simplest way I can think of right now would be to create a custom operator that'd pretty much extend refCount to add a callback.
Any better thoughts? I'm pretty sure that there's a better way of doing that.
Thanks!
I am not gonna lie, I was happy to find out I wasn't the only one looking for something like that. There is one another person.
I ended up doing something like that:
import { Observable } from 'rxjs';
export function tapTeardown(teardownLogic: () => void) {
return <T>(source: Observable<T>): Observable<T> =>
new Observable<T>((observer) => {
const subscription = source.subscribe(observer);
return () => {
subscription.unsubscribe();
teardownLogic();
};
});
}
And you use it like:
const augmented = connection.pipe(
tapTeardown(() => /* SOME TEARDOWN LOGIC */),
shareReplay({ bufferSize: 1, refCount: true }),
);
I've tried it and it seems to work correctly.
Here's how it's used:
import { of, timer } from 'rxjs';
import { map, publishReplay, take } from 'rxjs/operators';
import { refCountCb } from './refCountCb';
const source = timer(2000, 10000).pipe(
map(x => `Hello ${x}!`),
publishReplay(1),
refCountCb(() => console.log('MAIN CLOSED'))
);
source.pipe(take(1)).subscribe(x => console.log(x));
source.pipe(take(1)).subscribe(x => console.log(x));
Output:
Hello 0!
Hello 0!
MAIN CLOSED
I've built the custom refCountCb operator based on the source of refCount. It's basically just adding a callback so I won't copy paste the whole code here but it's available on the stackblitz.
Full demo: https://stackblitz.com/edit/rxjs-h7dbfc?file=index.ts
If you have any other idea please share it, I'd be glad to discover different solutions!

RX-JS - do we have in RX-JS method like .spy in bacon

Is there any function in RX-JS that serve same as we have .spy in Bacon.js.
https://github.com/baconjs/bacon.js/#bacon-spy
I need that to create Debugging tool for RX-JS.
Best Regards
Use do instance operator
Perform a side effect for every emission on the source Observable, but return an Observable that is identical to the source.
For example
// do
Rx.Observable.from([1,2,3,4,5,6])
.do(
// attach your function here
function(value){
console.log(value);
}
)
.subscribe((value) => {
console.log(value);
});

Converting callback hell to observable chain

I have been working with a convention where my functions return observables in order to achieve a forced sequential series of function calls that each pass a returned value to their following "callback" function. But After reading and watching tutorials, it seems as though I can do this better with what I think is flatmap. I think I am close with this advice https://stackoverflow.com/a/34701912/2621091 though I am not starting with a promise. Below I have listed and example that I am hoping for help in cleaning up with advice on a nicer approach. I am very grateful for help you could offer:
grandparentFunction().subscribe(grandparentreturnobj => {
... oprate upon grandparentreturnobj ...
});
grandparentFunction() {
let _self = this;
return Observable.create((observer) => {
...
_self.parentFunction().subscribe(parentreturnobj => {
...
_self.childFunction( parentreturnobj ).subscribe(childreturnobj => {
...
observer.next( grandparentreturnobj );
observer.complete();
});
});
});
}
parentFunction() {
let _self = this;
return Observable.create((observer) => {
...
observer.next( parentreturnobj );
observer.complete();
}
}
childFunction() {
let _self = this;
return Observable.create((observer) => {
...
observer.next( childreturnobj );
observer.complete();
}
}
The general rule-of-thumb in RxJS is that you should really try to avoid creating hand-made, custom Observables (i.e., using Observable.create()) unless you know what you're doing, and can't avoid it. There are some tricky semantics that can easily cause subtle problems if you don't have a firm grasp of the RxJS 'contract', so it's usually better to try to use an existing Observable creation function. Better yet, create Observables via applying operators on an existing Observable, and return that.
In terms of specific critiques of your example code, you're right that you should be using .flatMap() to create Observable function chains. The nested Observable.create()s you currently have are not very Rx-like, and suffer from the same problems 'callback hell'-style code has.
Here's an example of doing the same thing your example does, but in a more idiomatic Rx style. doStuff() is our asynchronous function that we want to create. doStuff() needs to call the asynchronous function step1(), chain its result into the asynchronous function step2(), then do some further operations on the result, and return the final result to doStuff()'s caller.
function doStuff(thingToMake) {
return step1(thingToMake)
.flatMap((step1Result) => step2(step1Result))
.map((step2Result) => {
let doStuffResult = `${step2Result}, and then we're done`;
// ...
return doStuffResult;
});
}
function step1(thingToMake) {
let result = `To make a ${thingToMake}, first we do step 1`;
// ...
return Rx.Observable.of(result);
}
function step2(prevSteps) {
let result = `${prevSteps}, then we do step 2`
// ...
return Rx.Observable.of(result);
}
doStuff('chain').subscribe(
(doStuffResult) => console.log(`Here's how you make a chain: ${doStuffResult}`),
(err) => console.error(`Oh no, doStuff failed!`, err),
() => console.debug(`doStuff is done making stuff`)
)
Rx.Observable.of(x) is an example of an existing Observable creator function. It just creates an Observable that returns x, then completes.

Resources