I have a problem with return Observable from second request.
Something like this:
commandRequest(action:string, commandData:any):Observable<CashDesckResponse>
{
let command:CashDeskRequest;
//ask my backend for command
this.requestCommandString(action, commandData, "POST")
//this is my response from backend
.map(r=>r.response).subscribe(my_1_response=>{
//then i need to send this response data to other server/action
//to execute it and return this second response as the result of
//this method.
command = new CashDesckRequest(my_1_response);
return this.server.executeCommand(command).map(return_this=>return_this);
});
};
private requestCommandString(action:string, requestData:any,
type:string):Observable<AjaxResponse>{
return Observable.ajax({
body:JSON.stringify(requestData),
method:type,
url:this._apiServerUrl+action
}).map(r=>r);
};
My problem that the commandRequest return value from first .map(). And if i`ll try to return value from inner subscribe compiller throw error:
[ts] A function whose declared type is neither 'void' nor 'any' must return a value.
https://habrastorage.org/web/93b/e6b/17b/93be6b17bf5b46b5847e67deefd6eea1.png
To map from one stream to another you can use mergeMap or switchMap
commandRequest(action:string, commandData:any):Observable<CashDesckResponse>
{
return this.requestCommandString(action, commandData, "POST")
.map(r1 => new CashDesckRequest(r1.response)
// map first response to command instance
.mergeMap(command =>this.server.executeCommand(command));
// map the command observable to a new observable
}
Related
I have an outer observable that i use with its result in the inner observable
and that I need to return the result from the inner observable
In the following example, I need to return the result allPersons from the second observable
the result from that function is Observable I want the it will return Observable
getAllPerson(): Observable<Data1[]> {
return this.dataService.getIds().subscribe(
(ids) => {
return this.dataService.getPersons().pipe(
map((allPersons) => {
console.log(ids);
//filter persons according to ids
return allPersons;
})
})
);
}
Also tried: and get
Argument of type 'Observable' is not assignable to parameter of type 'OperatorFunction<any, any>'.
getAllPerson(): Observable<any> {
return this. dataService.getIds().pipe(
switchMap((data) => {
this.dataService.getPersons().subscribe(
(allPersons) => {
console.log(ids);
//filter persons according to ids
return allPersons;
})
})
);
}
It's going to be somthing like that :
function getAllPerson(): Observable<Data1[]> {
return this.dataService.getIds().pipe(
switchMap((ids) => {
return this.dataService.getPersons().pipe(
map((allPersons) => {
return allPersons.filter(...); //filter persons according to ids
})
);
})
);
}
And subscribe the whole thing.
Okay, I see you're using TypeScript (nice), but there's a type error in the first two lines of your code.
getAllPerson(): Observable<Data1[]> {
return this.dataService.getIds().subscribe( /*more code here */ );
The type check is going to look at this is complain. It will say something like, "Denotationally, you've declared that this function returns an Observable object. By inference, I can see you're returning a Subscription object. As far as I can tell, Observable objects and subscription objects cannot be unified. This is a type error.
It's right.
The issue is that once you subscribe to an observable, you're no longer in RxJS land. You're left with an imperative way to end an observable, but you're done dealing with observable.
Think about subscribing as your way to exist the RxJS library. So if operators like of, from, fromEvent, new Subject, new BehaviorSubject, ect are ways to enter the RxJS library, then subscribe, lastValueFrom, firstValueFrom, behaviorSub.value, etc are ways to exit the RxJS library.
So how to avoid that dreaded subscribe. This is where RxJS hiher order operators come in. They let you chain, combine, merge, etc streams
for Example:
function getAllPerson(): Observable<Data1[]> {
return this.dataService.getIds().pipe(
switchMap(ids => this.dataService.getPersons().pipe(
map(allPersons => allPersons.filter(/* filter code ...*/))
))
);
}
I have a subject which emits a string value and the code is as below: when the components get initialized, the subjectTypeSubject is null. But there is another method in a component get subscribed to this observable where i set isLoading to true. Because the finalize is not getting called, the loading is always set to true. How to make it work so it gets completed when the value is null as well.
private subjectTypeSubject = new BehaviorSubject<string>(null);
private getPage() {
this.subjectTypeSubject.pipe(
filter((selectedSubjectType) => {
console.log('subject type', selectedSubjectType); //first time it comes as null. so it wont go inside switchmap.
return selectedSubjectType && selectedSubjectType !== '';
}),
switchMap((selectedSubjectType) => {
return this.customListsService
.getCustomListItemsByTypeName()
}),
map((customItemsData) => {
return customItemsData
})
);
}
private _getPage(pageNumber: number, search: string) {
this.loading = true;
this._pageSubscription = this.getPage({
pageSize: this._config.pageSize,
pageNumber,
search
})
.pipe(finalize(() => (this.loading = false))) //this is not called
.subscribe((p) => {
this._currentPage = p.pageNumber;
this.options = p.options;
this._allLoaded = p.isLast;
this.loading = false;
});
}
Adding a takeWhile() instead of filter worked for me. If there is any other better solution. please let me know. thanks
BehaviorSubject doesn't complete unless you complete it
There are multiple ways to call complete an observable in a pipe. take, takeWhile and takeUntil are some of them. Calling .complete on the BehaviorSubject is also an option.
But you should ask yourself: is this really what you want to achieve here? After completion it's not possible to pass any data to the subscription, even if the initial BehaviorSubject emits a new value.
One thing that this strange about your code: it should not work at all. In getPage() you are creating a new observable (by piping the BehaviorSubject), but you are not returning it. Therefore it should return undefined. It‘s also a little bit odd that you are using pipe in a function call. You should either declare the pipe during initialization or directly subscribe to a newly created observable.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 2 years ago.
I'm hoping somebody can point out the error of my ways here.
I have two functions at the moment. One is getData and it's an async function that simply makes an API call requesting data. The second function is getRandomCategories that encapsulates the getData function call and holds the value of that async operation in a variable called res. The rest of the code within getRandomCategories manipulates the response data to an array of numbers where each number represents a category.
When I use the debugger statement in the getRandomCategories function (right before the return statement within the try block) I'm getting the data type I'm expecting from my variable named apiCallCategoryArray - it's an array of numbers each representing a category. Life is good.
Here's the rub. When I call getRandomCategories expecting the dataArray variable (at the bottom of the code snippet) to hold an array of numbers - I'm getting a Promise back with its state pending??
I'm not understanding why the value for apiCallCategoryArray variable is showing up as my expected value using the debugger (and thus I'm returning it within the function) but I'm not able to access that value when I call the function. Why am I getting a Promise back with a pending state? What am I missing?
Here's my code below:
async function getData(endpoint, query, value) {
return await axios.get(
`http://jservice.io/api/${endpoint}?&${query}=${value}`
)
}
// createa a function that will return 6 random categories
async function getRandomCategories() {
try {
const res = await getData('categories', 'count', 50)
const data = res.data;
const categories = filterCategoryData(data); // I'm filtering for category id with clues_count === 5
const categoryIdArr = mapCategoryIds(categories); // an array of just category Ids
const shuffledCategoryIds = shuffle(categoryIdArr);
const apiCallCategoryArray = takeFirstXItems(shuffledCategoryIds, 6);
debugger// the value is what I'm expecting an array of numbers with length = 6
return apiCallCategoryArray
} catch (err) {
console.log(err);
}
}
//Solution one: Does not work. I'm getting a promise back instead of an array of numbers
const dataArray = getRandomCategories()
console.log(dataArray) // Promise { <state>: "pending" }
// expected return value [12231, 12344, 343245,124041, 12344, 348855] array of numbers
// Solution two: Does not work either. I'm still getttng a promise back instead of an array of numbers
const dataArray2 = getRandomCategories().then((array) => {
return array
})
console.log(dataArray2) // Promise { <state>: "pending" }
// expected return value [12231, 12344, 343245,124041, 12344, 348855] array of numbers
My objective is for my dataArray variable to hold an array of numbers (not a Promise with pending) returned by calling getRandomCategories(). So I can use this value for other functions in my code.
Thanks in advance for your time and responses.
you need to use use async, await to get back your Promise data. like this
async function test(){
let dataArray2 = await getRandomCategories();
console.log(dataArray2);
};
I'm calling a web service that returns a List. I want to return one item from that List, in a method. Essentially, when some function requests an instance of CollectorIssueBase, I want to retrieve ALL of them, cache them and return the one requested. But I can't figure out how to do it.
Here's my code:
public getByID(collectorID: string, id: string): Observable<CollectorIssueBase> {
return this.getAllMinimized(collectorID).pipe(
single(items => {
var item = items.find(i => i.ID == id);
return item;
})
);
}
The compiler keeps complaining that "Argument of type 'CollectorIssueValue[]' is not assignable to parameter of type 'CollectorIssueValue' which tells me right off the bat that I'm still returning an Array.
The value returned to the subscribing function is, indeed, an Array.
So what am I doing wrong? "single" seemed like the proper operator to use...am I using it wrong?
single callback takes CollectorIssueValue[] form each observable tick. If you want to change CollectorIssueValue[] to CollectorIssueValue i suggest use filter and map. Filter will filter empty arrays, and map will transform not empty array of CollectorIssueValue into CollectorIssueValue.
e.g.:
.pipe(
filter(arr => arr.length > 0),
map(arr => arr.find(...),
Is there a way to await for the first value returned from a subscribe call?
For example, in the following:
async getValue(...) {
myObs$ = ...
let val = await myObs$.pipe(first()).toPromise()
myObs$.subscribe(val => {
this.value = val
})
return val
}
The async function will return a Promise of val, but I also want to subscribe and get a data member value that will be updated whenever the observable myObs$ emits a new value.
My question: is there away to return a Promise without calling the line with the first() call and just wait for the first time I get the result in subscribe?
There's no real reason here that this method needs to be async as the caller will have to await it or use .then() regardless. You could simply rewrite it as:
getValue(...) {
myObs$ = ...
myObs$.subscribe(val => {
this.value = val
})
return myObs$.pipe(first()).toPromise()
}
This will keep your internal subscription around and allow it to keep receiving values while the caller of getValue will receive a Promise of the first emitted value.