有没有更有效的方式在 Typescript 的订阅中进行订阅 [英] Is There a More Efficient Way to Have a Subscription in a Subscription in Typescript
问题描述
我正在为 Angular 11 在 Typescript 中编写一些代码,我需要从一个 observable 中获取数据,然后通过另一个调用来获取名称.获得名称后,我想将其附加到原始对象中.
I am writing some code, in Typescript for Angular 11, where I need to get data from one observable and then looping through that have another call to get the name. Once I get the name I want to append it to the original object.
例如我有两个 observables 和 getSelfPurchases() 返回:
For example I have two observables and getSelfPurchases() returns:
{
id: 32
user_id: 5
script_id: 78
pp_id: "76SDI89768UIHUIH8976"
},
{
id: 33
user_id: 4
script_id: 79
pp_id: "78FGGF78SDF78FSD"
}
第二个 getScriptDetails(32) 返回:
and the second one, getScriptDetails(32), returns:
{
sname: "Spell Checker"
author: 43
price: 20.99
}
我已经成功地实现了我想做的事情,但我觉得它很草率且效率低下.我一直在阅读更多关于 RXJS 的东西,比如 switch map,但我不确定是否可以做这样的事情.或者也许我选择的方法已经是最好的.输入?
I have successfully achieved what I wanted to do but I feel like it is sloppy and inefficient. I've been reading more into RXJS stuff like switch map, but I am unsure if something like that can be done. Or maybe the method I chose is the best one already. Input?
this.userService.getSelfPurchases().subscribe(response => { // first observable
this.purchases = response;
this.purchases.forEach((purchase, index) => { // loop through our results
this.scriptService.getScriptDetails(purchase.script_id).subscribe(responseTwo => { // second observable
if (responseTwo[0] && responseTwo[0].sname !== undefined) {
this.purchases[index].sname = responseTwo[0].sname; // append to our original purchases object
}
});
});
});
推荐答案
您基本上不想嵌套订阅.这与其说是效率问题,不如说是可维护性、可扩展性和(最重要的)可读性.
You basically never want to nest subscriptions. It's not a matter of efficiency so much as it's a matter so much as maintainability, extendability, and (most importantly) readability.
嵌套订阅很快就会导致回调地狱.以这种方式绝望地迷失是非常简单的.虽然我想一切都有时间和地点.
Nested subscriptions quickly lead to call-back hell. It's surprisingly simple to get hopelessly lost that way. Though there's a time and place for everything I suppose.
这是您的代码 1-1 重新编写的代码,没有嵌套订阅.我将您的 purchases
数组映射到 getScriptDetails
调用数组,然后通过 merge
订阅该数组.
This is your code re-written 1-1 as you had it before, without nesting subscriptions. I map your array of purchases
into an array of getScriptDetails
calls and then subscribe to that array via merge
.
this.userService.getSelfPurchases().pipe(
tap(response => this.purchases = response),
map(purchases => purchases.map((purchase, index) =>
this.scriptService.getScriptDetails(purchase.script_id).pipe(
map(responseTwo => ({index, responseTwo}))
)
)),
mergeMap(scriptDetailsCalls => merge(...scriptDetailsCalls)),
).subscribe(({index, responseTwo}) => {
if (responseTwo[0] && responseTwo[0].sname !== undefined) {
// append to our original purchases object
this.purchases[index].sname = responseTwo[0].sname;
}
});
您可以将上面的 map 和 mergeMap 合并为一个 mergeMap,如下所示:
You can combine the map and mergeMap above into a single mergeMap as follows:
this.userService.getSelfPurchases().pipe(
tap(response => this.purchases = response),
mergeMap(purchases => merge(...
purchases.map((purchase, index) =>
this.scriptService.getScriptDetails(purchase.script_id).pipe(
map(responseTwo => ({index, responseTwo}))
)
))
)
).subscribe(({index, responseTwo}) => {
if (responseTwo[0] && responseTwo[0].sname !== undefined) {
// append to our original purchases object
this.purchases[index].sname = responseTwo[0].sname;
}
});
旁白:避免使用全局变量
这是对功能性纯度"的个人品味,但是避免设置全局变量然后稍后修改它的模式通常更干净.使测试变得更加困难,因为它使您对该全局变量的状态的保证较少.
Aside: Avoid global variables
It's a personal taste for functional "purity," but it's generally cleaner to avoid the pattern where you set a global variable and then modify it later. Makes testing it harder as it leaves you with fewer guarantees about the state of that global variable.
this.userService.getSelfPurchases().pipe(
mergeMap(purchases => forkJoin(
purchases.map(purchase =>
this.scriptService.getScriptDetails(purchase.script_id).pipe(
map(responseTwo => ({...purchase, sname: responseTwo[0].sname}))
)
)
))
).subscribe(purchasesWName =>
this.purchases = purchasesWName
);
这篇关于有没有更有效的方式在 Typescript 的订阅中进行订阅的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!