狀態模式中迴圈呼叫子元件時遇到的問題

李明發表於2022-12-01

先說一下問題場景,還是關於之前的調整時間之後再進行彈窗。
先說一下理想的效果:
未命名檔案 (4).jpg

首先要說明的就是不是每個任務都有後繼任務,就算有後繼任務如果後繼任務不符合條件(開始和截止時間啊都存在)也不會進行彈窗。
起初想靠彈窗元件來解決判定問題——彈窗開啟後進行判定,如果不符合條件再將彈窗關閉,雖然可以正常執行,並且基本上人眼看不出來進行過彈窗,但是多次試驗後發現仍會出現閃屏的情況,所以放棄了這個寫法。
先不論彈窗順序,首先試一下能不能正常呼叫彈窗。
一開始想的是在父元件中迴圈呼叫之前寫的batchSettingTime方法來解決。
先介紹一下這個方法:
需要的引數:更改前的時間,更改後的時間,當前任務,當前任務的後置任務。
這個方法首先獲取了當前任務的後置任務的任務詳情主要也就是獲取了這些任務的開始、截止時間,再去判斷這些任務中是否有需要展示的任務(開始/截止時間完備),如果有再去呼叫彈窗。

batchSettingTime(. . .) {
  // 後置任務中是否存在配置完全的任務
  havePostTasksAllDate = false;
  // 訂閱所有後置任務的任務詳情
  let ListOgPostTasks = [] as Observable<Task>[];
  postTasks.forEach(
    task => {
      ListOfPostTasks.push(
        this.taskStore
            .dispatch(fetchTaskDetail, task.id))} 
      )
    // 利用combineLatest獲取判斷獲取完所有的任務詳情後再去做處理
    combineLatest(ListOfPostTasks)
        .subscribe(
           data => {
             data.foreach(
               task => {
                    if(task.duedate && task.startDate) {
                      havePostTasksAllDate = true; 
                    }
               }
             )
    // 如果滿足條件再去呼叫彈窗
    if(havePostTasksAllDate) {
        const changeDate = newDate - oldDate;
        const dialogRef = this.util.thyDialog.open(xxxComponent, {將task,    changedate,oldDate傳入彈窗})
    }
    // 因為如果在關閉後進行呼叫的話由於彈窗元件已經銷燬,導致其中的變數獲取不到,所以要在其關閉前進行處理
    dialogRef.beforeClosed().subScribe
    if(dialofRef.componentInstance.selectedTasks.lenth != 0 && dialogRef.componentInstance.beSubmit) {
        dialogRef.componentInstance.selectedTasks.forEach(
            task => {
                this.taskDependentStore.fetchDependentTasks(task.id)
                this.taskDependentStore.select(TaskDepenDentStore.postDependentTasks)
                    .pipe(takeUntile(dialogRef.afterClosed())), skip(1))
                    .subscribe(data => {
                        // 由於要及時的取消訂閱舊的觀察者,所以當此彈窗關閉後取消訂閱
                        // 由於select會返回兩次內容,所以透過skip(1)來跳過獲取舊的內容
                        postTasks = data;
                        this.taskStore.dispatch(fetchTaskDetail, task.id)
                            .subscribe(
                                task => {
                                    this.batchSettingTime(task.dueDate - changeDate, task.dueDate, task, postTasks)
                                })
                            })
                        })
        }
    });
}

起初由於沒有及時取消對taskDependentStore.select(TaskDepenDentStore.postDependentTasks) 的訂閱導致每次執行fetchDependentTasks都會觸發訂閱導致每當關閉,開啟新彈窗都會觸發之前的訂閱,從而導致彈窗的多次額外彈出。

此外由於之前所說select方法正常情況下就會觸發兩次,可以由自訂變數來獲取第二次的內容而不獲取第一次的舊的內容。

但是這麼寫總歸還是不規範,並且也不方便測試,如果有像combineLatest的這種rxjs運算子,那麼應該也會有類似於只獲取第二次observable內容或者跳過第一次observable內容的運算子,查詢rxjs官網後發現果然有。

xxxObservable
    .pipe(skip(1))
    .subscribe({
        console.log('跳過了第一次傳送的內容')
    })

測試之後發現上面的程式碼如果在只有只有一個任務的情況下是可以正常執行的,比如:任務一=》任務二=》任務三=》任務四

當我們改變任務一,就會彈出任務二的彈窗,改變任務二就會有任務三的彈窗以此類推。

但是當我們改變多個並列任務時就會出現問題——一次性彈出四個彈窗,並且彈窗中內容也相互影響——任務二的後繼出現在了任務三彈窗中,任務三的後繼出現在了任務二彈窗中。
猜測也還是由於迴圈中的fetchDependentTasks執行多次導致的,並且在彈窗元件中也會獲取任務的後置任務及後置任務的任務詳情,並且這些也都是靠store獲取的,同時有多個元件同時呼叫快取就產生了混亂,所以猜測只要解決多個彈窗的問題,應該就能解決彈窗內任務混亂的問題。
比如拿兩個並列任務來舉例,其執行過程猜測如下。
未命名檔案 (5).jpg
但是這張圖肯定是有問題的,因為我們用了skip(1)來避免獲取舊資料,後來又去掉skip(1)再去執行發現彈窗生成了六次,這個問題仍在解決中。

那麼為什麼只有一個任務就沒問題呢?
因為其執行過程如下:
未命名檔案 (6).jpg

相關文章