Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/366.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在满足条件之前,无法使用计时器调用嵌套函数?_Javascript_Jquery_Angular - Fatal编程技术网

Javascript 在满足条件之前,无法使用计时器调用嵌套函数?

Javascript 在满足条件之前,无法使用计时器调用嵌套函数?,javascript,jquery,angular,Javascript,Jquery,Angular,我有两种方法可以调用一些API: getData(param) getAnotherData(分别来自get data) 下面的方法调用第一个方法: this.service.getData(param).subscribe(res=>{ this.variable=res; //在这里,基于第一个响应,我将调用第二个api this.service.getAnotherData(this.variable['data']).subscribe(res2=> //在这里,我将得到一个“开始”的响

我有两种方法可以调用一些API:

  • getData(param)
  • getAnotherData(分别来自get data)
  • 下面的方法调用第一个方法:

    this.service.getData(param).subscribe(res=>{
    this.variable=res;
    //在这里,基于第一个响应,我将调用第二个api
    this.service.getAnotherData(this.variable['data']).subscribe(res2=>
    //在这里,我将得到一个“开始”的响应
    //或者没有“开始”。如果没有“开始”,那么我必须打电话
    //直到我“开始”使用该API最多1分钟。
    )})
    
    我怎样才能做到这一点

  • 尽量避免嵌套订阅。相反,尝试使用更高阶的映射运算符,如
    switchMap

  • 您可以使用RxJS
    repeat
    takeWhile
    操作符再次订阅可观察对象,直到它满足一个条件

  • 试试下面的方法

    this.service.getData(param.pipe)(
    开关映射(res=>
    this.service.getAnotherData(res['data']).pipe(
    重复(),
    takeWhile(res=>res=='started')//poll`getAnotherData()`直到响应“started”
    )
    )
    ).订阅(
    res=>{},
    错误=>{}
    );
    
    编辑:toPromise的使用将被折旧,请参见此答案的注释


    如果包含调用的函数是
    async
    ,则在这种特殊情况下,可以使用
    wait
    等待结果

    const res=wait this.service.getData(param.toPromise();
    this.variable=res;
    让另一个数据;
    while(另一个数据!=='started'){
    anotherData=等待this.service.getAnotherData(this.variable['data']).toPromise();
    }
    
    <代码> > p>您可以考虑下面的方法使用<代码>定时器< /代码>运算符< /p>
      private readonly _stop = new Subject<void>();
      private readonly _start = new Subject<void>();
      trialTime = 60 * 1000;
      trialInterval = 500;
      timer$ = timer(0, this.trialInterval).pipe(
        takeUntil(this._stop),
        takeWhile( i => (this.trialTime / this.trialInterval) > i),
        repeatWhen(() => this._start),
      );
      start(): void { this._start.next();}
      stop(): void { this._stop.next();}
    
      responses = [];
    
      ngOnInit()  {
        const param = 1;
        this.timer$.pipe(
          mergeMap(() => this.service.getData(param)),
          switchMap(res => this.service.getAnotherData(res.data)),
          tap(res => this.responses.push(res)),
          tap(res => { if (res === 'started') { this.stop(); }}),
        ).subscribe();
      }
    
    private readonly\u stop=new Subject();
    private readonly_start=new Subject();
    试验时间=60*1000;
    trialInterval=500;
    计时器$=计时器(0,this.trialInterval).pipe(
    直到(到此为止),
    takeWhile(i=>(this.trialTime/this.trialInterval)>i),
    重复(()=>此。_开始),
    );
    start():void{this._start.next();}
    stop():void{this._stop.next();}
    答复=[];
    恩戈尼尼特(){
    常数参数=1;
    这个.timer$.pipe(
    mergeMap(()=>this.service.getData(param)),
    switchMap(res=>this.service.getAnotherData(res.data)),
    点击(res=>this.responses.push(res)),
    点击(res=>{if(res==='started'){this.stop();}}),
    ).subscribe();
    }
    
    上述方法将运行60秒,如果未启动,将自动停止

    用户还可以手动停止和启动可观察的


    如果我是你,必须对vanilla JS做类似的事情,我会使用以下方法:

    1. Subscribe to `getData` function call result
    2. In the scope of subscription handler:
      2.1 Add a new method to indicate if 1 minute has elapsed with initial value to `false`
      2.2 Use `setTimeout` to callback a function and update flag after a minute to `true`
      2.3 Create inline function which would call `getAnotherData` method and subscribe to it's result and within its subscription scope
        2.3.1 If result is started, clear timeout so that timeout event handler will be cleared
        2.3.2 If one minute is not elapsed, call inline function again
      2.4 call inline function
    
    比如:

    this.service.getData(param).subscribe(res => {
        this.variable = res;
        const minuteElapsed = false;
    
        // based on the first response
        const timeoutHandler = setTimeout(
            () => { minuteElapsed = true; },
            60000
        );
        const fetchAnotherData = () => {
            this.service.getAnotherData(this.variable['data']).subscribe(res2 =>  
                if (res === 'started') {
                    clearTimeout(timeoutHandler); // clean up
                    // do stuff!
                } else if (!minuteElapsed) {
                    fetchAnotherData(); // calling again!
                }
            });
        };
        fetchAnotherData(); // call inline function
    })
    

    与其他答案中提到的一样,尽可能避免嵌套订阅。我将使用rxjs
    expand
    操作符来实现这一点,我们将使用它重新运行第二个API请求,直到收到所需的结果

    expand
    将通过API请求中的观察值,根据条件,您可以让它停止额外处理或只是重复调用

    然后,我们可以使用
    takeUntil
    来实现对请求的超时。它可能看起来像这样(我借用了另一个答案中的脚手架):


    在这里玩一玩:

    好的,但在这里我有另一个场景轮询
    getAnotherData()
    ,直到响应“启动”,如果必须最多轮询1分钟,并且我们还可以取消此计时器或通过按钮调用>调用计时器,我们可以使用超时操作符吗?@Madpop:
    timeout
    没有帮助<代码>超时
    仅在没有发出时才会发出错误。在这里,可观察对象将连续发射,
    超时
    的计时器将在每次发射时重置。好的,但我们如何做到这一点,然后如果条件不满足,那么它必须调用api长达1分钟,直到条件变为元,并且在订阅中,如果条件满足,哪个方法响应将出现bcoz我必须调用方法i不建议使用
    toPromise()
    方法。它在RxJS 7中,在RxJS 8中。谢谢你,我不知道。似乎我们可以在将来使用
    等待lastValueFrom(this.service.getAnotherData(this.variable['data']))
    。@madpop你有机会看一下吗?
    this.service.getData(param).pipe(
      switchMap(res => 
        this.service.getAnotherData(res['data']).pipe(
          expand((result) => result === 'finished' ? EMPTY : this.service.getAnotherData(res['data']).pipe(delay(500))),
          takeUntil(timer(60000).pipe(
            // Here, you can react to the timeout
            tap(() => console.log('Timeout occurred'))
          )),
        )
      )
    ).subscribe(
      (res) => {
        /**
          Do whatever you want with the result, you need to check again for 'finished',
          since everything is passed through
         */
      }
      err => { }
    );