problème abstrait : chaque fois un émetteur et un événement observable source, une séquence d'appels d'API et Angular Services doit être déclenché. Certaines de ces invocations dépendent des résultats précédents. .
Dans mon exemple, la source observable startUpload$
Déclenche une série d'invocations.
Utiliser la déstructuration Ceci peut être écrit comme ceci:
this.startUploadEvent$.pipe(
concatMap(event => this.getAuthenticationHeaders(event)),
map(({ event, headers }) => this.generateUploadId(event, headers)),
tap(({ event, headers, id }) => this.emitUploadStartEvent(id, event)),
concatMap(({ event, headers, id }) => this.createPdfDocument(event, headers, id)),
concatMap(({ event, headers, id, pdfId }) => this.uploadBilderForPdf(event, pdfId, headers, id)),
mergeMap(({ event, headers, id, pdfId, cloudId }) => this.closePdf(cloudId, event, headers, id, pdfId)),
tap(({ event, headers, id, pdfId, cloudId }) => this.emitUploadDoneEvent(id, event, cloudId)),
).subscribe()
Il lit presque comme une approche impérative. Mais cela a certains problèmes:
{ event, headers, id, pdfId, cloudId }
generateUploadId(event, headers)
) sont nécessaires pour recevoir toutes les valeurs précédentes de manière à pouvoir les transmettre au tuyau suivant, même si la méthode elle-même n'exige pas_
private closePdf(cloudId, event, headers, id, pdfId) {
return this.httpClient.post(..., { headers } )
.pipe(
//...,
map(() => ({ event, headers, id, pdfId, cloudId }))
)
}
Ce serait bien si le compilateur pouvait prendre soin de la chaudière (comme avec async await
) Pour écrire le code qui se lit comme celui-ci (sans aucun des problèmes mentionnés ci-dessus):
private startUpload(event: StartUploadEvent) {
const headers = this.getAuthenticationHeaders(event)
const id = this.generateUploadId()
this.emitUploadStartEvent(id, event)
const pdfId = this.createPdfDocument(event, headers, id)
this.uploadBilderForPdf(event, pdfId, headers, id)
const cloudId = this.closePdf(headers, pdfId)
this.emitUploadDoneEvent(id, event, cloudId)
return cloudId
}
Comment passer des résultats entre observables chaînées sans les problèmes que j'ai mentionnés? Y a-t-il un concept RxJS que j'ai manqué?
Vous avez raison sur ces préoccupations et problèmes que vous avez mentionnés, mais le problème que je vois ici, vous transformez votre état d'esprit d'une approche impérative d'une approche réactive/fonctionnelle, mais examinons d'abord le code impératif en premier.
private startUpload(event: StartUploadEvent) {
const headers = this.getAuthenticationHeaders(event)
const id = this.generateUploadId()
this.emitUploadStartEvent(id, event)
const pdfId = this.createPdfDocument(event, headers, id)
this.uploadBilderForPdf(event, pdfId, headers, id)
const cloudId = this.closePdf(headers, pdfId)
this.emitUploadDoneEvent(id, event, cloudId)
return cloudId
}
Ici, vous voyez que le matériel est plus propre que vous avez event
que vous pouvez passer et obtenir uniquement ce que vous voulez et transmettez-le aux fonctions suivantes et que nous souhaitons déplacer ce code à l'approche réactive/fonctionnelle.
le principal problème de mon point de vue est que vous avez fait perdre votre fonction le contexte qu'ils ont par exemple getAuthenticationHeaders
ne devraient pas renvoyer le event
du tout, il ne devrait que retourner headers
et la même chose pour les autres fonctions.
lorsque vous traitez avec RXJS (AKA REACTIVE APPROCHE), vous avez un peu géré avec ces problèmes, ce qui est correct car il conserve les concepts fonctionnels appliqués et conserve votre code plus prévisible comme pure
opérateurs ne devraient traiter que des données au même. Pipeline qui maintient tout ce qui maintient pure et ne conduit pas aux effets secondaires qui conduiront à un code imprévisible.
Je pense que voulez-vous être résolu avec nested pipes
(c'est la meilleure solution de mon avis)
concatMap(event => this.getAuthenticationHeaders(event).pipe(
map(headers => this.generateUploadId(event, headers).pipe())
))
et il est fortement utilisé dans certaines bibliothèques backend rxjs comme - marble.js
vous pouvez utiliser une approche similaire à Result Selector
:
concatMap(event => this.getAuthenticationHeaders(event).pipe(
map(headers => ({ headers, event }))
)),
ou les grandes autres solutions suggérées par lesquelles les gens le feront fonctionneront, mais vous aurez toujours les mêmes problèmes que vous mentionnez, mais avec un code plus propre/lisible.
Vous pouvez également le tourner en async/await
Approche, mais vous perdrez la réactivité que RXJ vous fournit.
ce que je peux suggérer, c'est d'essayer de lire davantage sur la programmation réactive et de la façon dont vous déplacez votre état d'esprit et je vais fournir des liens ici que je vois est très bon de commencer et d'essayer certaines bibliothèques construites au sommet de RxJS Comme -- Cyclejs et je recommande de lire sur la programmation fonctionnelle qui aidera beaucoup aussi à ces bons livres Guide essentiellement adéquat sur FP (en JavaScript) & COMPOSANT LE LOGICIEL .
Je recommande cette excellente conversation recettes RxJS qui changera votre mode d'utilisation de RxJS.
Ressources utiles: