英文:
How to make 2 async calls with rxjs one after the other regardless if the first succeeds or fails
问题
以下是翻译好的部分:
我有一个使用案例,需要进行两个异步调用。这里的顺序很重要。第一个调用需要在开始下一个调用之前完成(或失败)。
下面是一个示例代码,展示了我需要的内容,但它并没有以响应式的方式完成(即不使用rxjs操作符)。
public performDBTasks(): Observable<void> {
return Observable.create(
(observer) => {
this.performDBTask1().subscribe(
() => {
this.performDBTask2().subscribe(
() => {
observer.next();
observer.complete();
},
() => {
observer.error();
}
);
},
(error) => {
this.performDBTask2().subscribe(
() => {
observer.next();
observer.complete();
},
() => {
observer.error();
}
);
}
);
}
);
}
更新:只是为了澄清,这两个调用类似于HTTP调用,因此它们将返回数据并完成或出错。不会有数据流。
英文:
I have a use case where I need to make two async calls. order is important here. The first call needs to complete (or fail) before starting the next call.
I have a example code below showing what I need but it is not done in a reactive way (ie. using rxjs operators)
public performDBTasks(): Observable<void> {
return Observable.create(
(observer)=> {
this.performDBTask1().subscribe(
()=> {
this.performDBTask2().subscribe(
()=> {
observer.next();
observer.complete();
},
()=> {
observer.error();
});
},
(error)=> {
this.performDBTask2().subscribe(
()=> {
observer.next();
observer.complete();
},
()=> {
observer.error();
});
});
});
}
Update: just to clarify the two calls are http like calls so they will return data & complete or error. There will be no stream of data.
答案1
得分: 1
You could use from
with concatMap
, where it will wait until the previous request has finished.
const { from, concatMap, fetch: { fromFetch }, tap } = rxjs;
const endpoints = [
// Returns 404
'https://dummy.restapiexample.com/api/v1/404-error',
// Returns 200
'https://dummy.restapiexample.com/api/v1/employee/1'
];
from(endpoints).pipe(
// Won't fetch until the previous request has finished.
concatMap(url => fromFetch(url).pipe(
tap(url => console.log(`Making request to: ${url.url}`)),
)),
tap(resp => console.log(`Code: ${resp.status}`))
).subscribe()
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.0/rxjs.umd.min.js"></script>
英文:
You could use from
with concatMap
, where it will wait until the previous request has finished.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const { from, concatMap, fetch: { fromFetch }, tap } = rxjs;
const endpoints = [
// Returns 404
'https://dummy.restapiexample.com/api/v1/404-error',
// Returns 200
'https://dummy.restapiexample.com/api/v1/employee/1'
];
from(endpoints).pipe(
// Won't fetch until the previous request has finished.
concatMap(url => fromFetch(url).pipe(
tap(url => console.log(`Making request to: ${url.url}`)),
)),
tap(resp => console.log(`Code: ${resp.status}`))
).subscribe()
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.0/rxjs.umd.min.js"></script>
<!-- end snippet -->
答案2
得分: 1
使用switchMap
和catchError
来同时执行performDBTask2
。
const { of, throwError, switchMap, tap, catchError } = rxjs;
const performDBTask1 = () =>
Math.random() > 0.5 // 50/50 出错的机会
? of('Success Task 1')
: throwError('Error Task 1');
const performDBTask2 = () => of('Success Task 2');
performDBTask1().pipe(
tap({
next: (val) => { console.log(val); },
error: (error) => { console.log(error); }
}),
catchError(() => performDBTask2()),
switchMap(() => performDBTask2())
).subscribe((val) => {
console.log(val);
});
每次点击上面的“运行代码段”按钮时,performDBTask1
失败的概率是50%,但performDBTask2
总是成功的。
英文:
Use a switchMap and a catchError to both return performDBTask2
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const { of, throwError, switchMap, tap, catchError } = rxjs;
const performDBTask1 = () =>
Math.random() > .5 // 50/50 chance of erroring
? of('Success Task 1')
: throwError('Error Task 1');
const performDBTask2 = () => of('Success Task 2');
performDBTask1().pipe(
tap({
next: (val) => { console.log(val); },
error: (error) => { console.log(error); }
}),
catchError(() => performDBTask2()),
switchMap(() => performDBTask2())
).subscribe((val) => {
console.log(val);
});
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.0/rxjs.umd.min.js"></script>
<!-- end snippet -->
Everytime you hit the Run code snippet
button above there is a 50/50 chance of performDBTask1 failing but performDBTask2 always succeeds.
答案3
得分: 0
你可以使用 switchMap 和 last 来解决你的问题。
const { Subject, merge, of } = rxjs;
const { switchMap, last, map, catchError } = rxjs.operators;
const first$ = new Subject();
const second$ = new Subject();
const result$ = first$.pipe(
// 捕获错误并返回一个观察者,该观察者获取“?”并之后完成
catchError(() => of("?")),
// 仅在可观察对象完成时发出,并返回最后一个值
last(),
// 切换到可观察对象 second$
switchMap((firstValue) =>
second$.pipe(
// 仅用于记录目的 - 记录 first$ 和 second$ 的值
map((secondValue) => [firstValue, secondValue])
)
)
);
result$.subscribe(console.log);
first$.next(1);
first$.next(2);
first$.complete();
second$.next(4);
信息
- 根据你的问题,我不知道你想要实现的确切输出是什么。你的问题只描述了异步调用顺序。如果输出不符合你的需求,请留下评论,我可以尝试适应。但请记住要指定/更新你的问题。
- 你的示例代码订阅了许多未处理的订阅。尽量避免尽可能多的订阅是一个良好的实践。理想情况下,每个 UI 更新或后端/服务调用最多只有一个订阅。有关如何在 Angular 9 中避免内存泄漏的更多信息,请阅读此问题:如何避免在复杂的rxjs管道中出现内存泄漏
- 通过
first$.error("msg")
创建错误是不可能的。相应的 StackOverflow 问题:如何在行为主题上引发错误并继续流程。随时模拟管道上的错误。这种模拟不是回答问题所必需的,所以我会避免实现模拟。
英文:
You can use switchMap and last for your problem.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
const { Subject, merge, of} = rxjs;
const { switchMap, last, map, catchError } = rxjs.operators;
const first$ = new Subject();
const second$ = new Subject();
const result$ = first$.pipe(
// catch an error and return an observable that gets "?" and completes afterwards
catchError(() => of("?")),
// only emit if observable completes and return last value
last(),
// switch to observable second$
switchMap((firstValue) =>
second$.pipe(
// just for logging purpose - log the first$ and second$ value
map((secondValue) => [firstValue, secondValue])
)
)
);
result$.subscribe(console.log);
first$.next(1);
first$.next(2);
first$.complete();
second$.next(4);
<!-- language: lang-html -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.5.3/rxjs.umd.min.js"></script>
<!-- end snippet -->
Info
- From your question I don't know what exact output you want to achieve. Your qeustions describes only the async call order. If the output does not fit your needs, comment and I can try to adapt. But please keep in mind to specify/udpate your question.
- Your sample code subscribes many subscriptions that are not handled. It is a good practice to avoid as many subscriptions as possible. In perfection you have maximum one subscription for every ui update or backend/service call. Read more in this question about how to avoid memory leaks with rxjs pipes
- *Creating the error via
first$.error("msg")
is not possible. Corresponding stackoverflow question: How do I throw an error on a behaviour subject and continue the stream. Feel free to be simulate an error on the pipe. The simulation is not needed to answer the question so I will avoid implementing a simulation.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论