skip to Main Content

I have a function which generates reports from the data of an order/request. The function inside has a loop to see the total number of impressions to make and then a series of actions to do.

The problem is that I can’t get it to be done linearly, that is, first finish a function before advancing to the next line.

The main function is:

async request(){
    for (let index = 1; index <= this.numLabels; index++) { 
      let time: number = 0;

      this.p_orderRequest.RequestId=this.generateRandomId().toString().substring(0,9);
      
      this.porderService.InsertRequest(this.p_orderRequest).subscribe(async data => {
        //Start interval with promise to ensure not continue in loop for??
          const responseRequest = await new Promise<void>((resolve, reject)=>{
            this.timerCheck = setInterval(() => {
            time = time +this.INTERVAL_DURATION_MS;
            
            if(data.type === 'success' || data.type ==='error'){              
                if(this.GetProtocol()){
                  console.log("Protocol correct")
                  if(this.CheckPrintEnd()){
                    if(this.blnFinished){
                      if(this.p_orderRequest.result=="OK"){
                                            this.porderService.SaveOrderRegisterControl(this.p_orderRequest).subscribe();
                        this.porderService.SaveSystemAudit(this.p_orderRequest).subscribe();
                      }             
                    this.porderService.DeleteRequest(this.p_orderRequest).subscribe();
                    }
                  }
                }
              
              time = 0;
              console.log("Cycle cycle completed.")
              clearInterval(this.timerCheck)
              resolve();
            }else if(time>this.DEFAULT_TIME_OUT){
              this.p_orderRequest.result="NG:TIMEOUT=" + time
              this.UpdateRequest(this.p_orderRequest, "TIME_OUT");
              clearInterval(this.timerCheck);
              resolve();
              }else{
              console.log("Waiting for the server...")
              }
            },1000);  
          })
         this.numLabelsPrinted++
        });
     }
  }

If I get a response from the server, I start a series of functions until the cycle ends and the interval closes so I can move on to the next print if there is one. If I do not get a response within the established time, I have a timeout configured, although it is not this part that is failing me.

Mostly, I have the problem in if(GetProtocol()) and if(CheckPrintEnd()) since it is first doing the console.log("Order cycle completed.") before GetProtocol() finishes, and therefore I can’t close the print as I need.

Those functions are these:

 GetProtocol(): any{
     this.porderService.GetProtocol(this.p_orderRequest.RequestId).subscribe(data => {
      if (data.type === 'success') {
        return true;
      } else {
        return false;
      }
    });
  }



  CheckPrintEnd():any{
    this.porderService.CheckPrintEnd(this.p_orderRequest).subscribe( data => {
     if (data.type === 'success') {
        this.p_clsRequest = JSON.parse(data.data.toString()) as POrderRequest;
        this.blnFinished = !!(this.p_orderRequest.porderValue);
        return true;
     } else {
       return false;
     }
   });
 }

I tried different things like do it with promises, return true/false in GetProtocol and CheckPrintEnd, Async/await…but nothing works good.

So, I want the code to run synchronously, first finishing one function before continuing to the next because the next one needs the values โ€‹โ€‹of the previous one.

2

Answers


  1. Below is my atempty to get rid of the mixing of promises and observables, I have used only rxjs to create a stream of values, that flow down to a single subscribe, do go through the docs for all the operators to better understand the implementation, but I have added comments, for you to better understand my implementation!

      request() {
        const apiArray$ = of([]);
        for (let index = 1; index <= this.numLabels; index++) {
          this.p_orderRequest.RequestId = this.generateRandomId()
            .toString()
            .substring(0, 9);
            // the below sequence gets executed for each element added by the for loop
          const apiSequence = forkJoin([
          this.porderService
          .InsertRequest(this.p_orderRequest),
          // subscribe cannot be used inside a function, which outputs to the if condition it will always be false, to solve this, we can use a fork join, to preevaluate the conditions simulatenously
          this.porderService.GetProtocol(this.p_orderRequest.RequestId).pipe(
            // great operator to transform the inner values of a stream!
            map(data => {
            if (data.type === 'success') {
              return true;
            } else {
              return false;
            }
          })),
          // subscribe cannot be used inside a function, which outputs to the if condition it will always be false, to solve this, we can use a fork join, to preevaluate the conditions simulatenously
          this.porderService.CheckPrintEnd(this.p_orderRequest).pipe(
            // great operator to transform the inner values of a stream!
            map(data => {
            if (data.type === 'success') {
               this.p_clsRequest = JSON.parse(data.data.toString()) as POrderRequest;
               this.blnFinished = !!(this.p_orderRequest.porderValue);
               return true;
            } else {
              return false;
            }
          }))
        ]).pipe(
            // great for sequential executions, first one runs, then the below code
            switchMap(([data, protocolBool, checkPrintEndBool]) => {
              if (data.type === 'success' || data.type === 'error') {
                if (protocolBool) {
                  console.log('Protocol correct');
                  if (checkPrintEndBool) {
                    if (this.blnFinished) {
                      if (this.p_orderRequest.result == 'OK') {
                        return this.porderService
                          .SaveOrderRegisterControl(this.p_orderRequest)
                          .pipe(
                            // great for sequential executions, first one runs, then the below code
                            switchMap(() => {
                              // assuming you want this to be executed after the top one!
                              return this.porderService
                                .SaveSystemAudit(this.p_orderRequest);
                            })
                          );
                      }
                      return this.porderService
                        .DeleteRequest(this.p_orderRequest)
                    }
                  }
                  // for all the other cases, we return the default value
                  // no case was executed!
                  return of(false);
                }
            })
          )
          // adding the APIS to the for loop
          apiArray$.push(apiSequence);
        }
        // runs this observable array in parallel, returns all the values at once!
        forkJoin(apiArray$).subscribe((output: any) => {
          this.numLabelsPrinted = output.length;
          console.log(output);
        });
      }
    
    Login or Signup to reply.
  2. If you are familiar with rxjs you could forkJoin or zip all the request together to one stream (Observable) and subscribe to it in one place.

    I’ve put together a quick example how I would solve this. The conditions or variables are just trying to replicate your code. Hope this gives you some inspiration to you.

    requestInit() {
        // Adding these two var just to replicate your code
        let blnFinished: boolean;
        let p_orderRequest: any;
    
        let TIMEOUT: number = 300000;
        const INTERVAL_DURATION_MS = 2000;
    
        const onDestroy$: Subject<void> = new Subject<void>();
    
        // Initialize timeout
        setTimeout(() => {
          onDestroy$.next();
          onDestroy$.complete();
        }, TIMEOUT);
    
    
        // GetProtocol, CheckPrintEnd, SaveOrderRegisterControl and SaveSystemAudit functions need to return an observable, I used of() just for demonstration
        const getProtocolStream$: Observable<any> = of('GetProtocol').pipe(mergeMap(() => of('CheckPrintEnd').pipe(mergeMap((value) => {
          if (value && blnFinished) {
            if (p_orderRequest.result == "OK") {
              return forkJoin(of('SaveOrderRegisterControl'), of('SaveSystemAudit'));
            } else {
              of('DeleteRequest');
            }
          }
        }))));
    
    
        // Main request, replace the of() with your InsertRequest
        return of(123)
        .pipe(
          // This is to make sure we unsubscribe when timeout reached 
          takeUntil(onDestroy$), 
          switchMap(() => interval(INTERVAL_DURATION_MS).pipe(
            mergeMap((data: any) => {
              if (data.type === 'success' || data.type === 'error') {
                return getProtocolStream$;
              } else {
                // This case will unsubscribe
                onDestroy$.next();
                onDestroy$.complete();
              }
        }))));
      }
    
      request(): void {
        const numLabels = 4;
        const observableStreamList: Observable<any>[] = [];
    
        Array.from(Array(numLabels)).forEach(() => {
          observableStreamList.push(this.requestInit())
        });
    
        forkJoin(observableStreamList).subscribe((value) => {
          // Triggered when all the streams are done
        })
      }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search