RxJS zip and pipe in combination not working - javascript

I have two server calls that I need to wait for. However, the user can decide if the second server call is even made. All of this is happening in an Angular Resolver, as the data is necessary for the next page.
The problem is that from my zip function the code never reaches the pipe, leaving me stuck in the resolver. Here is the procedure:
zip my requests
pipe them
and return an observable to my resolve function
this is my code:
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<any> | Promise<any> { //
const id = +route.paramMap.get('id');
const wantsSecondRequest = +route.paramMap.get('wantsSecondRequest');
const requests: Array<Observable<any>> = new Array();
requests.push(firstServerCallHappensHere());
if (wantsSecondRequest === 1) {
requests.push(secondServerCallHappensHere());
}
return zip(requests).pipe(take(1), mergeMap(([a, b]) => {
// DO STUFF WITH MY REQUESTS
return of(a);
}));
}
I tried it with Promise.all and working with Promises instead of Observables, but the issue with those are that I am unable to never complete the Promises in the case of an error, so that the navigation never happens.

According to the doc http://reactivex.io/documentation/operators/zip.html zip() accepts a number of parameters which are observables but you provide an array. It might help:
public resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):
Observable<any> | Promise<any> { //
const id = +route.paramMap.get('id');
const wantsSecondRequest = +route.paramMap.get('wantsSecondRequest');
return zip(
firstServerCallHappensHere(),
wantsSecondRequest === 1 ? secondServerCallHappensHere() : undefined,
).pipe(take(1), mergeMap(([a, b]) => {
// DO STUFF WITH MY REQUESTS
return of(a);
}));

Related

Resolving Promise on Page Load using route resolver

I'm using AngularFireAuth module to get the current user's UID to the component on page load through route resolving, but the lines of code needed to return the unwrapped promise to the component don't seem to be resolving and leave the promise unfulfilled.
relevant Routing Module Code for the edit-profiles module:
const routes: Routes = [
{
path: '',
component: EditProfilesComponent,
resolve: { userUID: UserInfoService },
},
];
The resolver service code is as follows:
export class UserInfoService implements Resolve<any> {
constructor(public afAuth: AngularFireAuth, public af: AngularFirestore) {}
async resolve() {
const user = await this.afAuth.currentUser;
const userUID = user?.uid;
return userUID; //returns undefined
}
}
I've also tried making it one promise and resolving it but that returned undefined as well. The code looked like this:
const promise = new Promise(async(resolve, reject) => {
const user = await this.afAuth.currentUser
const userUID = user?.uid;
resolve(userUID)
})
And then the EditProfiles consumer component code is:
export class UserInfoService implements Resolve<any> {
constructor(public afAuth: AngularFireAuth, public af: AngularFirestore) {}
async resolve() {
const user = await this.afAuth.currentUser;
const userUID = user?.uid;
return userUID; //returns undefined
}
}
I'm really not sure how to make it so that the promise returns the userUID to be used and consumed by that component, and display user-specific data, but I'm not sure how to make sure it's there before the component is instantiated.
#Bravo was right it was a problem with the design of the code it turns out what I actually wanted was for the page to wait until the promise was resolved to print out the data so what was needed on the ngOnInit was the following code
ngOnInit(): void {
this.afAuth.authState.subscribe((user) => {
if(user) {
//write logic here to make sure the promise gets resolved
} else {
//logic for when the page is getting the information
}
})
}
This is how you make sure that promises sent from firebase are resolved, you subscribe to the authstate observable and when that observable returns true, then you can finally retrieve the information from firebase that you need, that's the key piece that I was missing that it turns out that route resolvers may not have been the best tool for.

How to throttle typescript functions that return output

I am writing a node.js application using typescript. My application will have several services communicating with each other. Several of the services need to call an external API. This API has a restriction on the number of calls per second that can be executed. Because of this I want to create one service that wraps the external API calls (let's call it ApiService). The other services will call this one and it will collect their requests in a queue and execute them sequentially - N requests per second (for simplicity let's assume 1 per second). When service A calls a method of the ApiService - it expects to receive an output (it is fine to receive a Promise).
Now my question is - how to queue these API calls in the ApiService, so that every 1 second the next call in the queue is executed and also return the output of that API call to the caller of the ApiService?
Here's a sample service:
export class ServiceA {
apiService: ApiService;
public constructor(_apiService: ApiService) {
apiService = _apiService;
}
public async DoWork() {
// Do some stuff
const output: number = await apiService.RetrieveA(param1, param2);
// Do something with the output
}
}
The ApiService:
export class ApiService {
queue: (() => Promise<any>)[] = [];
public async RetrieveA(param1, param2): Promise<number> {
const func = async () => {
return this.CallApi(param1, param2);
};
this.queue.push(func);
return func();
}
public async RunQueue() {
while(true) {
const func = this.queue.shift();
if (!func) { continue; }
// Call the function after 1 second
await setTimeout(() => { func(); }, 1000);
}
}
private async CallApi(param1, param2): Promise<number> {
// Call the external API, process its output and return
}
}
The main method that orchestrates the whole thing:
var CronJob = require('cron').CronJob;
const apiService = new ApiService();
const service = new ServiceA(apiService);
new CronJob('* * * * * *', function() {
service.DoWork();
}, null, true);
apiService.RunQueue();
The problem I am facing is that when RetrieveA method returns func() - the function gets executed. I need to return a Promise but the actual function execution needs to happen in RunQueue() method. Is there a way to achieve this? Can I return a promise without executing the function right away and upon awaiting this promise - to receive the output when the function is called in the RunQueue method?
Or is there a different approach to solving my issue of throttling API calls that return output?
I am new to the Node.js/Typescript/JavaScript world, so any help is appreciated :)
All of that could be a lot simpler if you want to restrict calls to RetreiveA to 2 per second:
//lib is here: https://github.com/amsterdamharu/lib/blob/master/src/index.js
import * as lib from '../../src/index'
const twoPerSecond = lib.throttlePeriod(2,1000);
export class ApiService {
public RetrieveA(param1, param2): Promise<number> {
//removed the resolver part, according to the typescript signature
// it should return a promise of number but resolver actually takes
// that number and returns void (undefined?)
return twoPerSecond(this.CallApi.bind(this))([param1, param2]);
}
//change the signature of this function to take one parameter
// but deconsruct the array to param1 and param2
private async CallApi([param1, param2]): Promise<number> {
// Call the external API, process its output and return
}
}
Your method only works if there is only ever one instance of this class. If you were to create multiple instances and call RetrieveA on those instances you no longer limit requests to callApi.
I did manage to find a working solution. I'm not very familiar with the whole Promise and async concept in JavaScript, so this may not be the best solution, but it does the job for my specific case. Here is the code for anyone else looking to implement something similar:
The sample ServiceA remains the same as above:
export class ServiceA {
apiService: ApiService;
public constructor(_apiService: ApiService) {
apiService = _apiService;
}
public async DoWork() {
// Do some stuff
const output: number = await apiService.RetrieveA(param1, param2);
// Do something with the output
}
}
Here's the modified ApiService that returns promises for the output and throttles the actual function execution:
export class ApiService {
// We keep the functions that need to be executed in this queue
// and process them sequentially
queue: (() => void)[] = [];
public async RetrieveA(param1, param2): Promise<number> {
// This resolver serves two purposes - it will be called when the
// function is executed (to set the output), but will also be part
// of the Promise that will be returned to the caller (so that the
// caller can await for the result).
let resolver: (value: number) => void;
// This function will be executed by the RunQueue method when its
// turn has come. It makes a call to the external API and when that
// call succeeds - the resolver is called to return the result through
// the Promise.
const func = async () => {
return this.CallApi(param1, param2).then(resolver);
};
this.queue.push(func);
// This is the promise that we return to the caller, so that he
// can await for the result.
const promise = new Promise<number>((resolve, reject) => {
resolver = resolve;
});
return promise;
}
public async Run() {
this.RunQueue(this.queue);
}
private async RunQueue(funcQueue: (() => void)[]) {
// Get the first element of the queue
const func = funcQueue.shift();
// If the queue is empty - this method will continue to run
// until a new element is added to the queue
if (func) {
await func();
}
// Recursively call the function again after 1 second
// This will process the next element in the queue
setTimeout(() => {
this.RunQueue(funcQueue);
}, 1000);
}
private async CallApi(param1, param2): Promise<number> {
// Call the external API, process its output and return
}
}
I hope the comments in the code make it clear what (and how) I'm trying to achieve.

RxJs: Share an Observable using async / await

Using RxJs 5 and Angular 4.
I want to share an observable so that I only make 1 Http request, and I also want to await the calls so that I get the result when I request it. I have the following code:
export class DataService {
constructor(public http: HttpClient) {
this.getObservable();
}
public observable;
public getObservable() {
const url = "api/getData";
this.observable = this.http.get(`${this.baseUrl}${url}`).share()
}
public async hasData(data: DataEnum) {
const result = await this.observable.toPromise();
return result.filter(x => x === data).length > 0;
}
}
However many calls to hasData is resulting in many calls to our api end point. I am assuming that I have set observable to a shared observable, and when I call .toPromise() it will just get the cached value and make it a promise, which I can await.
Is this how it should work?
Due to how share works, observable is resubscribed on toPromise, ths produces new requests.
Promises already provide caching behaviour. Considering that promises are already using in service API, they can be used exclusively:
constructor(public http: HttpClient) {
this.getPromise();
}
public promise;
public getPromise() {
const url = "api/getData";
this.promise = this.http.get(`${this.baseUrl}${url}`).toPromise()
}
public async hasData(data: DataEnum) {
const result = await this.promise;
return result.filter(x => x === data).length > 0;
}
Your code seems overly complex to me. I would likely do something like:
private data = null;
getData():Observable<> {
// if data is already available, return it immediately
if (this.data) return Observable.of(this.data);
// else, fetch from the server and cache result
return this.http.get(url).do(data => this.data=data)
}
So whenever you want the data you just do:
this.getData().subscribe(
data => console.log(data);
)
To be sure that you won't call your API endpoints multiple times before the data arrives, you have a few options.
Look into data resolvers -- these will not init your component until the data has arrived. In ngOnInit the data will be ready synchronously so no risk of calling the server multiple times.
Alternatively, you can hide the view until the data is ready with *ngIf="data" so a user won't click a button multiple times.

Observable for mutiple responses in angular 2

So, I have this service which first calls a function from another module which basically returns an list of urls from an external api. This service then must http.get from all the urls in that list (every url returns a json object of same format) then return a single observable which I can then use in an angular component. Here's what my service code looks like:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import { Client } from 'external-api';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
let client = new Client();
#Injectable()
export class GbDataService {
constructor(private _http: Http) {
}
getGBData(): Observable<any> {
client.fetchUrls("").then(resp=> {
resp.forEach(url => {
//this._http.get(url).map(res => res.json);
// Create a single observable for every http response
});
}).catch(function(err){
console.log(err.message);
});
//return observable
};
}
http.get returns and Observable<Response> type but I couldn't find a way to create and return one Observable for all http.get responses. How can this be done ? Should I create an observable array then push() all the get response I get to the array?
EDIT: It doesn't really matters to me if responses are emitted one by one or all at once BUT there must be only a single Obeservable which emits the responses of all the http.get requests.
Further Edit: This is my fetchURLs method:
fetchURLs(): Promise<any> {
return new Promise((resolve, reject) => {
let _repos: Array<any>;
//scrapeTrendingRepos() is from https://github.com/rhysd/node-github-trend
scraper.scrapeTrendingRepos("").then(repos => {
repos.forEach(repo => {
let url = `https:api.github.com/repos/${repo.owner}/${repo.name}`;
_repos.push(url);
});
resolve(_repos);
}).catch(err => {
reject(err);
});
})
};
Have I implemented Promises in fetchURLs() right??
So, you make a request and get back an array of URLs that you then want to fetch all and get one response from?
Those are the types of things that RxJS excels at.
#Injectable()
export class GbDataService {
constructor(private _http: Http) {
}
getGBData(): Observable<any> {
return Observable
.fromPromise(client.fetchUrls()) // returns Observable<array>
.switchMap( urls => {
// convert your list of urls to a list of http requests
let urlRequests = urls.map( url => http.get(url) );
// combineLatest accepts an array of observables,
// and emits an array of the last results of each of the observables
// but the first emit only happens after every single observable
// has emitted its first result
// TLDR: combineLatest takes an array of Observables
// and will emit an array of those observable emissions // after all have emitted at least once
return Observable.combineLatest(urlRequests);
})
}).catch(function(err){
console.log(err.message);
});
//return observable
};
}
Further info:
Read up on the combineLatest observable. In this scenario, it accomplishes what you want of waiting for all its observable arguments to emit before emitting a single array. But if your observable arguments also emit multiple times, it may not do what you expect and you might want to try a different operator like forkJoin or zip.
Additionally
You might want to use switchMap rather than flatMap - if a new request for urls to fetch comes through, switchMap will cancel any requests currently in flight before sending the new list of requests.
Further Edit
Although your fetchURLs implementation can work in its current incarnation, you can simplify your method a bit if you wish by taking advantage of how promises work. In 'Promise-land', the then handler also returns a Promise, and that second Promise will resolve with whatever value you return from your then handler (this is the basic promise chaining concept). Using that knowledge, you can simplify your method to:
fetchURLs(): Promise<any> {
//scrapeTrendingRepos() is from https://github.com/rhysd/node-github-trend
return scraper.scrapeTrendingRepos("").then(repos => {
// since repos is an array, and you wish to transform each value
// in that array to a new value, you can use Array.map
return repos.map( repo => `https:api.github.com/repos/${repo.owner}/${repo.name}`);
});
}
if client.fetchUrls("") return a native Promise you may want to use snorkpete solution.
if not try to create an observable:
getGBData(): Observable<any> {
return Observable.create(observer => {
client.fetchUrls("").then(resp=> {
resp.forEach(url => {
this._http.get(url).map(res => res.json).subscribe(data=>{
observer.next(data);
});
});
}).catch(function(err){
console.log(err.message);
observer.error(err);
});
});
}

How to use an observable in angular 2 guards' canActivate()

I have created an authentication guard for my angular2 rc5 application.
I am also using a redux store. In that store I keep the user's authentication state.
I read that the guard can return an observable or promise (https://angular.io/docs/ts/latest/guide/router.html#!#guards)
I can't seem to find a way for the guard to wait until the store/observable is updated and only after that update return the guard because the default value of the store will always be false.
First try:
#Injectable()
export class AuthGuard implements CanActivate {
#select(['user', 'authenticated']) authenticated$: Observable<boolean>;
constructor() {}
canActivate(): Promise<boolean> {
return new Promise((resolve, reject) => {
// updated after a while ->
this.authenticated$.subscribe((auth) => {
// will only reach here after the first update of the store
if (auth) { resolve(true); }
// it will always reject because the default value
// is always false and it takes time to update the store
reject(false);
});
});
}
}
Second try:
#Injectable()
export class AuthGuard implements CanActivate {
#select(['user', 'authenticated']) authenticated$: Observable<boolean>;
constructor() {}
canActivate(): Promise<boolean> {
return new Promise((resolve, reject) => {
// tried to convert it for single read since canActivate is called every time. So I actually don't want to subscribe here.
let auth = this.authenticated$.toPromise();
auth.then((authenticated) => {
if (authenticated) { resolve(true); }
reject(false);
});
auth.catch((err) => {
console.log(err);
});
}
}
When you subscribe to an observable, you can provide a callback function; in the example below, I call it CompleteGet. CompleteGet() will only be invoked on a successful get that returns data and not an error. You place whatever follow on logic you need in the callback function.
getCursenByDateTest(){
this.cursenService
.getCursenValueByDateTest("2016-7-30","2016-7-31")
.subscribe(p => {
this.cursens = p;
console.log(p)
console.log(this.cursens.length);
},
error => this.error = error,
() => this.CompleteGet());
}
completeGet() {
// the rest of your logic here - only executes on obtaining result.
}
I believe you can also add a .do() to the observable subscription to accomplish the same thing.
all you need to do is force the observable to update:
canActivate(): Observable<boolean> {
return this.authenticated$.take(1);
}
Edit:
canActivate waits for the source observable to complete, and (most likely, I don't know what happens behind the scenes), the authenticated$ observable emits .next(), not .complete()
From documentation: http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#instance-method-take
.take(1) method takes first value emitted by the source observable and then completes
Edit2:
I just looked at snippet you pasted, and I was right - the store.select() observable never completes, it always emits .next
Subscribe doesn't return an Observable.
However, you can use the map operator like that:
this.authenticated$.map(
authenticated => {
if(authenticated) {
return true;
}
return false;
}
).first() // or .take(1) to complete on the first event emit

Categories

Resources