У вас слишком много подписчиков.:)
Однажды я услышал несколько хороших советов по поводу Observables - «с подозрением относиться к подписке внутри подписки».Это очень хороший совет, потому что, вероятно, это означает, что я неправильно подхожу к проблеме, если делаю это.Как общее практическое правило, я также склонен не подписываться внутри сервиса, а оставить его для компонента (или, что еще лучше, только для шаблона компонента).В противном случае существует слишком много возможностей для утечек памяти, пытаясь удостовериться, что я отписал их всех.
В вашем случае я рад, что вы указали rxjs 5.5, потому что именно тогда был введен оператор .pipe, и это сделает вашкод значительно проще писать я думаю.Я многого не знаю о вашем коде, поэтому я предлагаю следующее не в качестве решения «вырезать и вставить», а в качестве примера того, как его реорганизовать, чтобы убрать все подписки из вашего сервиса и в конечном итоге вернутьнаблюдаемый, на который можно подписаться в вашем компоненте, как вы указали в своем вопросе.
Вот код, о котором вы можете подумать:
public registerAndGetToken() {
return this.initializeRegistration().pipe( // <-- return an Observable
catchError((error) => {
// handle case when initializeRegistration gives back an error,
// for example:
return throwError(`initializeRegistration() threw error: ${error.message}`);
// This assumes the error will be bubbled up to the component
}),
mergeMap((match) => {
const callbackResult = this.initializationCallback(match);
if(callbackResult) {
return this.renewToken().pipe(
tap((tokenResult)=> {
// Your example never uses tokenResult for anything ...
// so I'll assume you actually want tokenResult to bubble
// up all the way your component as the result ...
this.renewTokenCallback(); // This makes no sense to me ...
// why have a callback here?
}),
catchError((tokenError) => {
// add code to handle renewToken() returning an error
return tokenError;
})
)
}
else {
// return something that can be handled inside the component
// when callbackResult is false.
// for example:
return throwError('callbackResult is false');
}
})
)
}
Обновление - я решил поделиться некоторымиразмышления о том, почему для каждого шага, в случае, если это полезно.
- Общая структура такова, что, когда компонент подписывается на эту цепочку,
initializeRegistration()
станет исходной (или внешней) наблюдаемой, которая отбрасывает вещии после его завершения renewToken()
будет отображен в цепочку и предоставит окончательный результат в качестве возвращаемого токена. - Начните с оператора
return
, потому что эта функция будет посвящена настройке одной наблюдаемойцепочка, которая возвращается и на которую можно подписаться из компонента. - Далее начнем с
initializeRegistration()
.После подписки компонента эта функция будет выполнена, и цепочка будет ожидать продолжения, пока не завершится (или не выдаст ошибку). - Далее проверьте наличие ошибки.Это необходимо сделать на этом этапе, потому что мы собираемся отобразить (изменить) наблюдаемое в цепочке с помощью mergeMap, поэтому перед тем, как мы это сделаем, мы проверим источник наблюдаемого на наличие ошибок и исправим их.
- Далее, mergeMap,Этот оператор заботится о «подписке вверх», неявно подписываясь на
initializeRegistration
, когда он, в свою очередь, подписывается компонентом, поэтому нам не нужен этот вложенный шаблон подписки в вашей исходной функции.Этот оператор также отображает новое наблюдаемое (в данном случае возвращаемое значение из renewToken()
в нашу цепочку. Поэтому мы больше не проходим по match
, теперь мы проходим по tokenResult
в цепочке. - Прежде чем поместить результат обратно в основную цепочку, мы передадим это через два дополнительных оператора (я делаю это здесь, в подцепи, а не в главной цепочке из-за логики if-else):
- Firstоператор субцепи -
tap
. Здесь я действительно не понимаю вашу бизнес-логику и могу ошибаться в этом. Потому что касание на самом деле не влияет на цепочку, а просто дает нам точку вставки, где мы можемвыполнить какой-то побочный эффект, это то, что я решил назвать renewTokenCallback()
. В зависимости от того, что эта функция делает, это может быть неправильным способом справиться с этим ... - Следующий оператор суб-цепочки это
catchError
, это проверка на ошибки из renewToken()
- Теперь выполняется с подцепью, которая возвращается и снова возвращается к главной цепочке
- Finсоюзник - это
else
- важно, чтобы мы возвращали фактическую наблюдаемую изнутри эту область, которую можно объединить обратно в основную цепочку.Что это за наблюдаемое должно быть неясно - какое-то сообщение вплоть до компонента, сообщающее, что что-то пошло не так в callbackResult
Надеюсь, это поможет.