使用管道异步总是卡在加载

我正在使用joined$switchMap

在一个全局combineLatest中加入多个可观察值

这是组件TS文件

export class ProgressComponent implements OnInit{

    user: User;
    joined$: Observable<any>;

    constructor(
        protected tasksService: TasksService,protected courseService: CoursesService,protected fieldService: FieldsService,protected sessionService: SessionsService,protected applicationService: ApplicationForSessionsService,protected TaskdatesService: SessionTaskDatesService,protected router: Router,private userStore: UserStore)
    {}

    ngOnInit(): void {
        this.user = this.userStore.getUser();
        this.joined$ = this.applicationService.list(1,10,undefined,this.user.id)
            .pipe(
                switchMap(applications => {
                    const courseSessionIds = uniq(applications.map(application => application.courseSessionId))

                    return combineLatest(
                        of(applications),combineLatest(
                            courseSessionIds.map(courseSessionId => this.sessionService.get(courseSessionId).pipe(
                                switchMap(session => {
                                    const courseId = session.courseId
                                    return combineLatest(
                                        of(session),combineLatest(
                                            this.courseService.get(courseId).pipe(
                                                switchMap(course => {
                                                    const fieldId = course.fieldId

                                                    return combineLatest(
                                                        of(course),combineLatest(
                                                            this.fieldService.get(fieldId).pipe(
                                                                map (field => field)
                                                            )
                                                        )
                                                    )
                                                }),map(([course,field]) => {
                                                    return {...course,field: field.find(f => f.id == course.fieldId)}
                                                })
                                            )
                                        ),)
                                }),map(([session,course]) =>  {
                                    return {
                                        ...session,course: course.find(c => c.id === session.courseId)
                                    }
                                }),switchMap( session => {
                                    const sessionId = session.id;

                                    return combineLatest(
                                        of(session),combineLatest(
                                            this.TaskdatesService.getBySessionId(sessionId).pipe(
                                                switchMap(dates => {
                                                    const taskDatesIds = uniq(dates.map(dt => dt.taskId));

                                                    return combineLatest(
                                                        of(dates),combineLatest(
                                                            taskDatesIds.map(taskDateId => this.tasksService.get(taskDateId).pipe(
                                                                map(task => task)
                                                            ))
                                                        )
                                                    )
                                                }),map(([dates,task]) => {
                                                    return dates.map(date => {
                                                        return {...date,task: task.find(t => t.id === date.taskId)}
                                                    })
                                                
                                                })
                                            )
                                        )
                                    )
                                }),dates]) => {
                                    return {
                                        ...session,dates: dates.map(date => date.find(d => d.sessionId === session.id))
                                    }
                                })
                            ))
                        )
                    )
                }),map(([applications,session]) => {
                    return applications.map(app => {
                        return {
                            ...app,session: session.find(s => s.id === app.courseSessionId)
                        }
                    })
                })
            );
    }
}

这是 HTML模板文件

    <ng-container *ngIf="joined$ | async; else loading; let joined">

    <div *ngFor="let application of joined">
        <div class="current-application">
            <nb-card>
                <nb-card-header>
                    <h4>{{application.session.course.name}}</h4>
                    <small><i>{{application.session.course.field.name}}</i></small>
                </nb-card-header>
                <nb-card-body>
                    <p><b>id: </b>{{application.id}}</p>
                    <p><b>applicationDate: </b>{{application.applicationDate}}</p>
                    <p><b>acceptedDate: </b>{{application.acceptedDate}}</p>
                    <hr>
                    <p><b>session Id: </b>{{application.session.id}}</p>
                    <p><b>session capacity: </b>{{application.session.capacity}}</p>
                    <p><b>session startDate: </b>{{application.session.startDate}}</p>
                    <p><b>session endDate: </b>{{application.session.endDate}}</p>
                    <hr>
                    <p><b>Course Id: </b>{{application.session.course.id}}</p>
                    <p><b>Course Id: </b>{{application.session.course.id}}</p>
                </nb-card-body>
            </nb-card>
        </div>
    </div>

</ng-container>

<ng-template #loading>
    <p>Loding ...</p>
</ng-template>

编辑:调试后,我发现日期数组为空时会发生错误,因此解决方案包括对dates数组的长度进行测试。问题是当我尝试建立条件时出现以下错误

类型'(dates:SessionTaskDate [])=> void'的参数不能分配给类型'(value:SessionTaskDate [],index:number)=> ObservableInput'的参数。 不能将类型“ void”分配给类型“ ObservableInput”。

触发以下内容:

switchMap(dates =>{
        if(dates.length > 0){
            dates.map(date => this.augmentDateWithTask(date))
        }
    })
iCMS 回答:使用管道异步总是卡在加载

如果我理解正确,您有一系列应用,它们由this.applicationService.list返回的Observable通知。

然后每个应用程序具有一个 courseSessionId ,您可以使用它们通过this.sessionService.get方法获取课程会话详细信息。

然后每个 session 都有一个 courseId ,您可以使用它们通过this.courseService.get获取 course 的详细信息。

然后每个课程都有一个 fieldId ,您可以通过this.fieldService.get来获取 field 的详细信息。

现在,您应该有一个 session 数组,其中还包含它们所引用的 course 的所有详细信息。

然后,对于每个会话,您需要通过this.TaskdatesService.getBySessionId获取日期

日期似乎是包含 taskDatesId 的对象。您会收集所有 taskDatesIds ,并通过this.tasksService.get获取 task 详细信息。

现在您已经拥有所有日期和所有任务,对于每个 date ,您都将创建一个带有 date 的新对象>属性及其相关的任务

然后返回 session ,创建一个具有所有 session 属性及其相关的 dates 的新对象。

现在您有了一个包含所有 session 详细信息,它所引用的 course 所有详细信息以及它的 dates 所有详细信息的对象有。

最后一步是为每个 application 创建一个新对象,该对象包含 application 的所有属性以及“增强的” session 刚创建的对象。

很合理。

因此,再次,如果这是正确的理解,我将从最内在的要求向外解决问题。

第一个内部请求是一个从 course 开始返回一个Observable的请求,该对象发出具有所有 course 属性和 field 的对象>详细信息(我们将此对象称为 augmentedCourse )。可以通过这样的方法来执行

augmentCourseWithField(course) {
  return this.fieldService.get(course.fieldId).pipe(
    map (field => {
      return {...course,field}
    })
  )
}

然后我将向外走一步,并创建一个方法,该方法从 courseId 开始,返回一个Observable,该对象发出一个 augmentedCourse ,即具有所有课程字段详细信息。

fetchAugmentedCourse(courseId) {
  return this.courseService.get(courseId).pipe(
     switchMap(course => this.augmentCourseWithField(course))
  )
}

现在,让我们进一步做进一步的工作,并创建一个方法,该方法从 session 开始,返回一个具有 session 所有属性以及 session 所指的 augmentedCourse 。我们将此对象称为 augmentedSession

augmentSessionWithCourse(session) {
  return this.fetchAugmentedCourse(session.courseId).pipe(
    map(course => {
      return {...session,course}
    })
  )
}

现在再往外走一步。我们想从 courseSessionId 开始获取 augmentedSession

fetchAugmentedSession(courseSessionId) {
  return this.sessionService.get(courseSessionId).pipe(
     switchMap(session => this.augmentSessionWithCourse(session))
  )
}

那么,到目前为止我们取得了什么成就?我们能够创建一个Observable,它从 courseSessionId 开始发出 augmentedSession

在最外层,尽管我们有一个应用程序列表,每个应用程序都包含一个 courseSessionId 。不同的应用可以共享相同的 course ,因此具有相同的 courseSessionId 。因此,获取所有 application ,创建唯一的 courseSessionIds 列表,使用此类列表获取所有 courses 然后分配给每个课程都是有意义的应用及其课程。通过这种方式,我们避免了针对同一课程多次查询后端。

可以这样实现

fetchAugmentedApplications(user) {
  return this.applicationService.list(1,10,undefined,user.id).pipe(
    switchMap(applications => {
      const courseSessionIds = uniq(applications.map(application => application.courseSessionId));
      // create an array of Observables,each one will emit an augmentedSession
      const augSessObs = courseSessionIds.map(sId => fetchAugmentedSession(sId))
      // we can use combineLatest instead of forkJoin,but I prefer forkJoin
      return forkJoin(augSessObs).pipe(
        map(augmentedSessions => {
          return applications.map(app => {
            const appSession = augmentedSessions.find(s => s.id === app.courseSessionId);
            return {...app,session: appSession}
          });
        })
      )
    })
  )
}

使用类似的样式,您应该还可以在会话中添加日期详细信息。同样在这种情况下,我们从最内部的操作开始,即通过his.tasksService.get给定 taskIds 的数组来检索 tasks 的数组。

代码看起来像这样

fetchTasks(taskIds) {
   taskObs = taskIds.map(tId => this.tasksService.get(tId));
   return forkJoin(taskObs)
}

向外移动一步,我们可以用 date task 扩展 date 数组中的每个 date / em>这样

augmentDates(dates) {
  if (dates.length === 0) {
    return of([]);
  }
  const taskDatesIds = uniq(dates.map(dt => dt.taskId));
  return fetchTasks(taskDatesIds).pipe(
    map(tasks => {
      return dates.map(date => {
        return {...date,task: task.find(t => t.id === date.taskId)}
      })
    })
  )
}

现在,我们可以像这样用日期扩展会话

augmentSessionWithDates(session) {
  return this.TaskdatesService.getBySessionId(session.id).pipe(
    switchMap(dates => augmentDates(dates).pipe( 
      map(augmentedDates => {
        return {...session,dates: augmentedDates};
      })
    ))
  )
}

我们现在可以完成fetchAugmentedSession方法,并添加带有 date 信息的 session 扩展部分

fetchAugmentedSession(courseSessionId) {
  return this.sessionService.get(courseSessionId).pipe(
     switchMap(session => this.augmentSessionWithCourse(session)),switchMap(session => this.augmentSessionWithDates(session)),)
}

通过这种方式,您可以将逻辑分成较小的块,这些块更易于测试,并且希望更易于阅读。

我没有任何游乐场可以测试代码,因此很可能其中有错别字。我希望逻辑很清楚。

,

有很多异步嵌套,也许还有另一种方法可以解决这个问题?

如果您尝试获取多个可观测对象并进行单个订阅(例如,能够以类似方式对多个事件做出反应),请使用rxjs merge。

如果您要进行很多单独的订阅,例如订阅用户,foo和bar,然后将其所有值用于某种逻辑,请使用rxjs forkJoin。

如果您声明一个明确的目标,则提供指导会比较容易,但是我知道rxjs运算符嵌套的级别很难维护。

本文链接:https://www.f2er.com/1647033.html

大家都在问