2016-02-04 37 views
2

我正在重構一些原本沒有編寫的遺留代碼,並且我遇到了異步數據加載的問題。第一次打開一個特定的模式時,表示一個表單對象的一堆數據被加載。然後一個函數循環遍歷表單的輸入,並根據需要將其充實。它看起來是這樣的(極度簡化):有條件的異步調用

component.inputs.forEach(function(input) { 
    if (input.field == 'foo') { 
     input.cols = 5; 
     //etc. 
    } 

    if (input.field == 'bar') { 
     DataService.getBars().then(function(data){ 
      data.forEach(function(e){ 
       input.options.push(e.description); 
      }); 
     }; 
    } 

    if (input.field == 'baz') { 
     input.pattern = /regex/; 
     //etc. 
    } 
}); 

return component; 

的問題,當然,如果我的input.field是「酒吧」,代碼繼續運行,打異步調用的DataService前的最後返回已解決,因此第一次打開模式時,input.options尚未填入'bar'輸入。

是否有可能讓代碼等待來自DataService的承諾在繼續之前被解析,或者有另一種方式來處理這種情況:在大多數情況下,該函數是同步的,但必須在該函數中進行異步調用只有一種情況?或者,我是否通過在這個大型的ifs鏈中包含異步調用來打擊自己?

+0

這裏真正的問題是,您應該進行一次寧靜的調用以返回所有需要的潛在數據。一旦你有了數據,你應該繼續進行映射,最後返回模態。 –

+0

@David L.一個有趣的觀點。我想我可以將整個函數包裝在DataService調用的.then中,但是不管這些數據是否需要,我們都必須進行調用。此外,這個方法非常大(上面的例子是一個很大的簡化),所以它會很笨拙,不友好。 –

+0

我會改變對象結構,所以如果你不需要打電話,你不會。因爲它現在存在,你在這種模式流行音樂中效率非常低。 –

回答

0

如果您想保留現有的代碼結構並使其工作,您可能需要使用promises。您也可以使用javascript的map function。注意:您需要將$q注入您想調用此功能的任何位置。

function getComponent() { 
    var deferred = $q.defer(), 
     deferred2 = $q.defer(), 
     promises = component.inputs.map(function(input)) { 
      if (input.field == 'foo') { 
       input.cols = 5; 
       deferred2.resolve(); 
      } 
      else if (input.field == 'bar') { 
       DataService.getBars().then(function(data) { 
        data.forEach(function(e){ 
         input.options.push(e.description); 
        }); 
        deferred2.resolve(); 
       }).catch(function(err)) { 
        deferred2.reject(err); 
       }); 
      } 
      else if (input.field == 'baz') { 
       input.pattern = /regex/; 
       deferred2.resolve(); 
      } 

      return deferred2.promise; 
     }); 

    $q.all(promises) 
     .then(function() { 
      deferred.resolve(component); 
     }).catch(function(err) { 
      deferred.reject(err); 
     }); 

    return deferred.promise; 
} 

一旦component.inputs每個input已經適當地解析,那麼$q.all塊將觸發,你可以返回你的新component對象。

最後,設置你的component對象,只需做到以下幾點:

getComponent().then(function(result)) { 
     //Set component object here with result 
     $scope.component = result; 
    }).catch(function(err) { 
     // Handle error here 
    }); 
1

一種方法是創建一個承諾,其附加的屬性,你的返回對象。

function getComponent() { 
    component.inputs.forEach(function(input) { 
     //create initial promise 
     var $promise = $q.when(input); 
     if (input.field == 'foo') { 
      input.cols = 5; 
      //etc. 
     } 
     if (input.field == 'bar') { 
      //chain from initial promise 
      $promise = $promise.then(function() { 
       //return promise for chaining 
       return getBarPromise(input); 
      }); 
     } 
     //attach promise to input object 
     input.$promise = $promise; 
    }); 

    var promises = []; 
    angular.forEach(inputs, function(input) { 
     promises.push(input.$promise); 
    }); 
    //create composite promise 
    var $promise = $q.all(promises); 

    //final chain 
    $promise = $promise.then(function() { 
     //return component for chaining 
     return component; 
    }); 
    //attach promise to component 
    component.$promise = $promise; 

    return component; 
}; 

返回的component對象最終將填入服務調用的結果。需要等待所有服務電話完成的功能可以從$promise屬性中獲得。

$scope.component = getComponent(); 

$scope.component.$promise.then(function (resolvedComponent) { 
    //open modal 
}).catch(function(errorResponse) { 
    //log error response 
}); 

,因爲調用一個承諾的then方法返回一個新派生的承諾,這是很容易可以創建承諾的一個鏈條。有可能創建任意長度的鏈,並且由於承諾可以通過另一個承諾來解決(這會進一步延遲其解決方案),因此可以在鏈中的任何時刻暫停/推遲解決承諾。這使得實現強大的API成爲可能。 1