rxjsangular8angular-promise

execute a sequence of GET calls to an API wait and do some treatments on the results then give the result as argumant to another methode for a POST


I am new to Angular and i am facing some difficulties with a task. I have an array of IDs that i want to execute the same GET Call over. And for every GET call result i have to do some operations and then add the result of every operation to some arrays. I managed to find a way to do it correctly. But my problem is, i can't manage to wait for the final result to be ready (after all the GET calls are done and the operations too) before giving it as an argument to another method that will send it with a POST call.

the method where i do the GET calls and the operations over every call's result (the problem occurs when i am in the rollBackSPN condition).

async getComponentIds(taskName: String, selectedComponents: IComponent[]) {
    const componentsId: number[] = [];
    const componentsWithoutParams: IComponent[] = [];
    let sendPortaPrecedente : boolean;

    if(taskName == "rollBackSPN"){
      from(selectedComponents).pipe(
        concatMap(component =>{
          return this.http.get<any>("Url"+component.idComponent).pipe(
            tap(val => {
              sendPortaPrecedente = true;

              for(const obj of val){
                if((obj.name == "z0bpqPrevious" && obj.value == null) || (obj.name == "datePortaPrevious" && obj.value == null) || (obj.name == "typePortaPrevious" && obj.value == null)){
                  sendPortaPrecedente = false;
                }
              }

              if(sendPortaPrecedente){
                componentsId.push(component.idComponent);
              }else{
                componentsWithoutParams.push(component);
              }
            }),
            catchError(err => {
              return of(err);
            })
          )
        })
      ).subscribe(val => {
        return { componentsId : componentsId, componentsWithoutParams : componentsWithoutParams, sendPortaPrecedente : sendPortaPrecedente};
      });

    }else{
      for (const component of selectedComponents) {
        componentsId.push(component.idComponent)
        return { componentsId : componentsId, componentsWithoutParams : componentsWithoutParams, sendPortaPrecedente : sendPortaPrecedente};
      }
    }
  }

The method where i pass the getComponentIds(taskName: String, selectedComponents: IComponent[]) result so it can be send with a POST call (again when i am in the rollBackSPN condition)

executeTask(serviceIdSi: string, actionIdSi: string, actionClassName: string, componentName: string, taskName: string,
     componentsId: number[], componentsWithoutParams: IComponent[], sendPortaPrecedente: boolean): Observable<any> {
    
    const url = this.taskUrl + `?serviceId=${serviceIdSi}` + `&actionId=${actionIdSi}` + `&actionClassName=${actionClassName}`
      + `&componentName=${componentName}` + `&taskName=${taskName}`;
    
    
      if(taskName == "rollBackSPN"){

        if(sendPortaPrecedente && componentsWithoutParams.length == 0){
          return this.http.post<any>(url, componentsId);

        }else{

          let errMessage = "Some Error Message"

          for(const component of componentsWithoutParams){
            errMessage = errMessage + component.idComponent +"\n";
          }
          throw throwError(errMessage);

        }

      }else{
        return this.http.post<any>(url, componentsId);

      }
    
  }

Both these methods are defined in a service called TaskService. And the service is called like this in a component UnitTaskButtonsComponent.

async launchUnitTask() {
    this.isLoading = true;
    this.isClosed = false;
    this.appComponent.currentComponentIndex = this.componentIndex;
    let res = await  this.taskService.getComponentIds(this.unitTaskLabel, this.selectedComponents);
      
      
    this.taskService.executeTask(this.appComponent.currentService.identifiantSi,
      this.appComponent.currentAction.identifiantSi,
      this.appComponent.currentAction.className,
      this.selectedComponents[0].name,
      this.unitTaskLabel,
      res.componentsId,
      res.componentsWithoutParams,
      res.sendPortaPrecedente).subscribe(
      data => this.executeTaskSuccess(),
      error => this.executeTaskError());
  }

"res" properties are always undefined when it's a rollBackSPN task.


Solution

  • The main issue here is that getComponentIds does not return a Promise. So awaiting does not work. I would suggest to change getComponentIds so that it returns an Observable instead.

    getComponentIds(taskName: string, selectedComponents: IComponent[]) {
      //                        ^^^^^^ use string instead of String
      return forkJoin(
        selectedComponents.map((component) => {
          return this.http.get<any>("Url" + component.idComponent).pipe(
            map((val) => {
              let sendPortaPrecedente = true;
    
              for (const obj of val) {
                if (
                  (obj.name == "z0bpqPrevious" && obj.value == null) ||
                  (obj.name == "datePortaPrevious" && obj.value == null) ||
                  (obj.name == "typePortaPrevious" && obj.value == null)
                ) {
                  sendPortaPrecedente = false;
                }
              }
    
              return { component, sendPortaPrecedente }
            }),
            catchError((err) => of(err))
          );
        })
      ).pipe(
        map((result) => {
          const componentsId: number[] = [];
          const componentsWithoutParams: IComponent[] = [];
    
          for (const val of result) {
            if (val.sendPortaPrecedente) {
              componentsId.push(val.component.idComponent);
            } else {
              componentsWithoutParams.push(val.component);
            }
          }
          return { componentsId, componentsWithoutParams };
        })
      );
    }
    
    async launchUnitTask() {
      this.taskService
        .getComponentIds(this.unitTaskLabel, this.selectedComponents)
        .pipe(
          switchMap((res) => {
            this.taskService
              .executeTask(
                this.appComponent.currentService.identifiantSi,
                this.appComponent.currentAction.identifiantSi,
                this.appComponent.currentAction.className,
                this.selectedComponents[0].name,
                this.unitTaskLabel,
                res.componentsId,
                res.componentsWithoutParams,
                res.sendPortaPrecedente
              )
          })
        ).subscribe(
          (data) => this.executeTaskSuccess(),
          (error) => this.executeTaskError()
        );
    }
    

    In the launchUnitTask method, we don't use await anymore. Instead, we call getComponentIds and chain the call of executeTask with a switchMap.