Я новичок в Angular и TypeScript, поэтому вопрос может звучать неправильно. Я использую Visual Studio Code и Angular 5 (ng-version = "5.2.11").
Мне нужно «подождать», пока мой вызов api не закончится, и вернуть результат, не оборачивая все в гигантский «.subscribe». Что у меня есть:
exampleMethod(): SettingsClass {
const myComplexObject: SettingsClass = {
field1: predefined.field1,
field2: predefined.field2,
field3: isComplexCalculationsNecessary ? this.CallApi_1(predefined.paramForField3) : predefined.field3,
};
return myComplexObject;
}
Раньше у меня были все параметры в «предопределенный», но теперь мне нужно запросить один (или несколько) из внешнего источника и немедленно вернуть «myComplexObject» из метода (к какому-то другому вызову api, или другому компоненту, или даже интерфейсу, но я нужно, чтобы этот объект был полностью определен со всеми заданными параметрами). Я мог бы перевернуть свой код «вверх ногами» и поместить все в большой «.subscribe», но я этого не сделаю, потому что в какой-то момент мне понадобится другой параметр для запроса, и все это сломается. Как написать вызов api, чтобы не переписывать весь мой код каждый раз, когда мне нужно добавить новый внешний вызов? Что-то вроде:
CallApi_1(paramForField3: string): Observable<int> {
return this.http.get(`${httpConfig.route}?$apply=filter${paramForField3}/groupby${httpConfig.groupingParam}`);
}
Или, может быть
CallApi_1(paramForField3: string): Observable<int> {
return this.ExternalCallsService.GetParam3Information(paramForField3).subscribe(res =>
.GetParam3Information contains the same http call as above, but I need to do
something to return this result outside, I don't know what);
}
Я ищу короля:
field3: isComplexCalculationsNecessary ? **await** this.CallApi(predefined.paramForField3) : predefined.field3,
В настоящее время я пробую rxjs, у которых есть интересные возможности для работы с Observables, такие как forkJoin, но я не уверен, что полностью смотрю в правильном направлении, возможно, такой трюк невозможен, или мое понимание Observables неправильно и мне нужно перенести сложную логику на бэкэнд? Пожалуйста, порекомендуйте.
Еще раз важно отметить, что простой ".subscribe" - это не то, что я ищу, потому что во всех примерах подписки мы не возвращаем значения, а присваиваем их какой-то глобальной переменной или непосредственно html-элементу, и это НЕ то, что я нужно, мне нужно получить ценность и продолжить работу с ней, как только внешний ресурс вернет ее.
хм, описание switchmap выглядит многообещающе, попробую.
Вы можете использовать rxjs/concat
, взгляните на этот пример: https://stackblitz.com/edit/angular-functions-in-sequence?file=src%2Fapp%2Fapp.component.ts
Это было бы что-то вроде этого: first, second и так далее - это функции, которые возвращают Observables
let sequence = concat([
this.first,
this.second,
this.afterSecond,
this.third]);
sequence.subscribe(currentFunction => {
currentFunction().subscribe(value => {
this.values.push(value);
})
});
Спасибо за предложение. Я мог ошибаться, но "this.values" выглядит как стандартная глобальная переменная, которая не может быть возвращена, присваивается только в какой-то момент (и если я назначу ее слишком рано, я получу null или undefined, а фактический результат будет Потерянный). Но я приведу пример, который вы привели.
this.value создается на уровне компонента и обновляется в NgOnInit для этого примера :) реальная реализация будет иметь функцию, которая возвращает Observable типа значений;)
Вы можете использовать операторы zip или forkjoin (они похожи, но не одинаковы) или вы можете связать свои HTTP-вызовы:
this.http.get1( url1, headers ).subscribe(
data => {
this.data1 = data;
this.http.get2( url2, headers ).subscribe(
data => {
this.data2 = data;
},
error2{
// do something on error2
}
)
},
error1 => {
// do something on error1
}
)
Это не очень хорошо, но работает нормально. Я помню, как использовал zip для решения той же проблемы.
Обновлено: я нашел пример zip
const tpcomprob$ = this.data.getTPComprob( this.tpcomprobsId);
const comprobs$ = this.data.getComprobs(this.tpcomprobsId);
this.sbsComprobs = zip(tpcomprob$, comprobs$, (tpcomprob: any, comprobs: any) => ({tpcomprob, comprobs}))
.subscribe(pair => {
this.tpcomprob = pair.tpcomprob;
this.comprobs = pair.comprobs;
},
error => {this.httpError = error ;})
});
Спасибо за ваш ответ, и это, вероятно, сработает. Но «this.tpcomprob» и «this.comprobs» являются глобальными переменными, которые будут присвоены в определенный момент времени. Я мог быть уверен, что они будут назначены одновременно, но не в том, что они будут назначены вообще, поэтому, когда я попытаюсь их использовать - они все равно могут или не могут быть пустыми, без какого-либо способа гарантировать (или заставить мои код для выполнения внешних запросов ждать). Наверное, мое понимание асинхронных операций, которое у меня есть из .net, здесь не подходит. Я вернусь к этой теме после некоторых исследований.
Из того, что я вижу здесь, rxmarbles.com/#zip zip будет ждать, пока 2 наблюдаемых объекта не завершатся и не выдадут или не выдадут ошибку. Таким образом, будут определены как tpcomprob, так и comprob.
Посмотрите на rxmarbles.com/#concat в этом случае, concat также будет работать, поскольку httpClient.get будет генерировать и завершать, но я думаю, что концептуально это неверно.
forkJoin - самый простой.
var _arr = [];
_arr.push(this.mySvc.callback01());
_arr.push(this.mySvc.callback02());
forkJoin(_arr).subscribe(resList=>{
//-- the response will be in array
});
я думаю, что
switchmap
- это то, что ты хочешь