簡體   English   中英

JavaScript嵌套的承諾范圍和AngularJS

[英]JavaScript nested promise scoping and AngularJS

我在JavaScript承諾中遇到了一個真正的問題,這是我在最近幾個小時內一直在嘗試解決的問題,但似乎無法解決。 我對諾言的經驗有限,因此我對自己的做法完全錯誤的想法持開放態度。

現在,我正在構建一個需要兩步過程的應用程序:

  1. 連接到外部PaaS服務,該服務將返回承諾
  2. 在該承諾范圍內,檢索一些數據

這是我創建的工廠的示例:

app.factory('serviceFactory', [
    function() {
        var getData = function getData() {
            service.connect(apiKey).then(function() {
                    service.getData('dataStore').then(function(result) {
                        // Retrieve data
                        return result;
                    }, errorFunction);
                },
                errorFunction);
        };
        return {
                getData: getData
        };
    }

]);

如您所見,這里有嵌套的承諾。 導致我出現問題的是,當我嘗試使用AngularJS視圖中最深層嵌套的諾言中的數據時。 具體來說,我想在ng-repeat語句中使用該諾言中的數據。 但是,無論我嘗試什么,它都不會顯示。 我試圖在promise中分配數據而不是返回,就像這樣:

service.getData('dataStore').then(function(result) {
    // Retrieve data
    // Assigned the enclosing scope's this to 'self'
    self.data = result;
}, errorFunction);

那也不行。 我嘗試了多種其他方法,但似乎無法將這些數據顯示在視圖中。 讓它顯示在console.log(data)調用中沒有問題,所以我知道數據可以正確返回。 有沒有人有解決這樣的問題的經驗?

我建議您盡量避免嵌套承諾。 您可以看一下這篇博客文章 ,該文章將使您了解如何避免“無極湯”,而可以實現承諾鏈。

關於您的問題,我建議以下內容:

一個快速的解決方案是解決您的問題。 您返回錯誤的工廠方法:

app.factory('serviceFactory', [
    function() {
        var getData = function getData() {
            return service.connect(apiKey).then(function() {
                    service.getData('dataStore').then(function(result) {
                        // Retrieve data
                        return result;
                    }, errorFunction);
                },
                errorFunction);
        };//here you should close the 'getData method
        return {
            getData: getData
        };   
    }
]);

但是,您可以重構代碼以鏈接您的諾言。 就像是:

app.factory('serviceFactory', [
    function() {
        var connect = function connect() {
            return service.connect(apiKey);
        };
        var getData = function getData(data) {
            return service.getData(data);
        };       
        return {
            getData: getData,
            connect: connect
        };
    }
]);

現在,您可以執行以下操作:

serviceFactory.connect(apiKey)
     .then(serviceFactory.getData)
     .then(function(result){
           //use data here
      })

所有這些都應該經過測試-如果您想要一個可行的解決方案,則可以添加一個插件或jsbin ...

編輯

我認為您在這里還有另一個問題。 您正在serviceFactoryservice之間混合service 我不確定我是否了解這是同一服務還是誰。 您能否提供更詳細的代碼或添加plunker / jsbin等。

我已經編輯了這個答案,我最初刪除了這個答案,因為我沒有很清楚地解釋我的意思,並且贏得了一些反對意見(沒有解釋,但這是我的猜測)。 無論如何,這是一個更完整的答案。

我懷疑您的問題是您使用的任何PaaS都不了解Angular,而Angular同樣也不了解PaaS。 您在問題中說,PaaS具有返回承諾的方法,但是如果Angular不知道這些承諾,那么當承諾解決時,Angular不知道更新DOM。 Angular通過摘要循環執行此操作,在該摘要循環中,Angular會檢查其正在監視的所有內容以查看其是否已更改。 當使用$q (或像其他角服務$http ),角知道自動揭開序幕消化周期時,他們解決。 但是,通過其他方式創建的承諾無法解決時,它不會啟動摘要循環。

我認為這就是您的代碼中正在發生的事情。 您的PaaS給您的諾言已經很好地解決了(您說可以通過控制台查看結果),但是您的HTML尚未更新。

我修改了我們正在研究的plunkr ,以演示其實際效果。 我創建了一個模擬PaaS(不知道您在使用什么),它使用jQuery創建了promise並解決了它們。 如您所見,當promise解析時,結果將記錄到控制台,但DOM尚未解析。

angular.module("app",[])
  .value("mockPaaS", mockPaaS)
  .factory("dataFactory", function($q, mockPaaS){

    function getData(){
      return mockPaaS.connect()
        .then(mockPaaS.getData);
    }
    return {
      getData: getData
    }
  })
  .controller("DataController", function (dataFactory) {
      var vm = this;
      dataFactory.getData().then(function(result){
        console.log(result);
        vm.dataArr = result;
      });
  })
  .directive("myApp", function(){
      return {
        bindToController: true,
        controller: "DataController",
        controllerAs: 'myApp',
        template: "<div ng-repeat='i in myApp.dataArr'>{{i}}</div>"
    };
  });

我最初建議您可以在捕獲諾言的結果后添加$scope.$apply()來解決此問題。 我分叉了Plunker,您可以在這里看到它的確,實際上是在更新DOM。

  .controller("DataController", function ($scope, dataFactory) {
      var vm = this;
      dataFactory.getData().then(function(result){
        console.log(result);
        vm.dataArr = result;
        $scope.$apply();
      });
  })

但是,還有一個慣用的解決方案。 當您從Angular中獲得需要在Angular中使用的promise時可以使用$ q.when (Angular知道的Promise)包裝該Promise ,並且當外部Promise解析時,Angular應該自然地開始它的摘要周期。

  .factory("dataFactory", function($q, mockPaaS){
    function getData(){
      return $q.when(mockPaaS.connect()
        .then(mockPaaS.getData));
    }
    return {
      getData: getData
    }
  })
  .controller("DataController", function (dataFactory) {
      var vm = this;
      dataFactory.getData().then(function(result){
        console.log(result);
        vm.dataArr = result;
      });
  })

本·納德勒(Ben Nadel) 在這里對此問題做了很好的解釋。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM