访问数组中承诺的值

Accessing values of promises in an array

本文关键字:承诺 数组 访问      更新时间:2023-09-26

我正在尝试从http请求创建一个数组,该数组包含一个name属性和两个承诺:一个作为数组,另一个作为对象。我能够使用这种方法获取我需要的信息,但我无法访问它以在 html 范围内显示它。例如,当我注销数组时,"people"我得到一个对象数组(看起来像:[Object,Object,Object]),我必须展开一堆东西才能看到每个对象的实际值,这样"person.skills"实际上必须是"person.skills.$$state.value"。此外,在页面上,将显示 {{person.name}},但其他两个只是空对象,如下所示:{}。那么我如何访问承诺的值,以便我可以只使用 {{person.skills}} 来显示数组呢?

.js

var getPeople = function() {
            var named = $q.defer();
            $http.get('/getnames').success(function (response) {
                named.resolve(response);
            });
            return named.promise;
        };

        getPeople().then(function(namesRes) {
            var people = [];
            names = namesRes;
            names.forEach(function(index){
                var name = index;
                var count = $q.defer();
                var skills = $q.defer();
                var urls = '/getskillsbyname/' + name;
                var urlc = '/getcountbyname/' + name;
                $http.get(urls).success(function (response) {
                    skills.resolve(response);
                });
                $http.get(urlc).success(function (response) {
                    count.resolve(response);
                });
                people.push({name:name, skills:skills.promise, count:count.promise});
            });
            return people;
        }).then(function(people) {
            console.log(people);
            $scope.people = people;
        });

.html

<div ng-repeat="person in people">
                <p>{{person.name}}</p>
                <p>{{person.skills}}</p>
                <p>{{person.count}}</p>
</div>

您的方法未正确返回 promise,您需要使用 $q 来等待所有内部承诺完成。

我已经通过在 forEach 循环中维护 grand promise 变量来实现您的代码,每当询问skills并发出couts调用时,它都会将该调用放入$q.all,并且$q.all承诺被移动到grandPromiseArray

var getPeople = function() {
    return $http.get('/getnames');
};
getPeople().then(function(response) {
  var people = [];
  names = response.data;
  grandPromiseArray = [];
  names.forEach(function(index) {
    var name = index, count = $q.defer(), skills = [],
        urls = '/getskillsbyname/' + name, urlc = '/getcountbyname/' + name;
    grandPromiseArray.push(
      $q.all([$http.get(urls), $http.get(urlc)])
      .then(function(response) {
        people.push({
          name: name,
          skills: response[0].data, //response[0] value returned by 1st promise
          count: response[1].data //response[1] value returned by 2nd promise
        });
      })
    );
  });
  return $q.all(grandPromiseArray).then(function() {
    return people
  });
})
.then(function(people) {
  console.log(people);
  $scope.people = people;
});

有效承诺链的示例:

function getSomeData(){
   var defer = $q.defer();
   $http.get(urls).success(function (response) {
      defer.resolve(response);
   });
   return defer.promise;
}

然后,您可以访问承诺数据,例如:

getSomeData().then(function(data){
   console.log(data); // This is the response of the $http request
});

在函数中组织您的请求,它看起来更干净。

默认情况下,$http也返回 promise,但我建议创建一个服务/工厂,它执行您的请求,然后您需要延迟。

$http.get(urls).then(function(data){
});

承诺不会自动允许您在 Angular 中显示结果。

Promise 是一个对象,它允许您通过传入最终触发并传递值的函数将异步操作链接在一起。

你不能要求myPromise.value并期望它在那里,因为这是一个异步过程,可能需要 20 毫秒、2 分钟,或者可能永远不会回来。

就其结构而言,如果您将数据获取部分分解到服务中,并且只是将服务注入控制器,则可能会更清晰,更容易推理。

我不确定你一直在使用哪个版本的 Angular,但我希望它至少是 1.2。
另外,我的例子是使用本机 Promise 构造函数,但我确信 Angular 的 $q 版本现在拥有所需的一切,如果您不使用 promise polyfill。

function PersonService (http) {
  function getResponseData (response) { return response.data; }
  function getURL (url) { return http.get(url).then(getResponseData); }
  function makePerson (personData) {
    return {
      name: personData[0],
      skills: personData[1],
      count: personData[2]
    };
  }
  var personService = {
    getNames: function () { return getURL("/names/"); },
    getSkills: function (name) { return getURL("/getskillsbyname/" + name); },
    getCounts: function (name) { return getURL("/getcountsbyname/" + name); },
    loadPerson: function (name) {
      return Promise.all([
        Promise.resolve(name),
        personService.getSkills(name),
        personService.getCount(name)
      ]).then(makePerson);
    },
    loadPeople: function (names) {
      return Promise.all(names.map(personService.loadPerson));
    }
  };
  return personService;
}

以下是我的人员服务可能的样子。
我编写了几个不断被重用的超小辅助函数。然后,我使服务完全是为了获取人员或有关人员的详细信息。
我正在使用Promise.all,并传递给它一系列承诺。当数组中的每个承诺都完成后,它会返回一个由承诺返回的所有数据的数组。 Promise.resolve用于一个地方。它基本上返回一个自动成功的承诺,以及它被赋予的值。当你需要一个承诺来启动一个链时,这使得它非常有用,但除了返回你已经拥有的值之外,你不必做任何特别的事情。我的假设是,q现在以与规范相同的方式命名其方法,并且 Angular 的$q实现也遵循规范。

function MyController (personService) {
  var controller = this;
  controller.people = [];
  controller.error = false;
  init();
  function setPeople (people) {
    controller.people = people || [];
  }
  function handleError (err) {
    setPeople([]);
    controller.error = true;
  }
  function init () {
    return personService.getNames()
      .then(personService.loadPeople)
      .then(setPeople)
      .catch(handleError);
  }
}

我的控制器现在变得非常非常简单。它使用服务来获取姓名并加载人员,然后设置他们,就可以使用了。 如果有错误,我会以任何有意义的方式处理它。

处理这些东西的注入非常快速和简单:

angular.module("myExample")
.service("PersonService", ["$http", PersonService])
.controller("MyController", ["PersonService", MyController]);

在页面上使用它现在也是无痛的:

<div ng-controller="MyController as widget">
  <ul ng-hide="widget.people.length == 0">
    <li ng-repeat="person in widget.people">
      <person-details person="person"></person-details>
    </li>
  </ul>
  <div ng-show="widget.error">Sorry, there was an error with your search.</div>
</div>

我认为您可以使用$q.all来构建您的人员:

var p = $q.all({
  name: $q.when(name),
  skills: $http.get(urls),
  count: $http.get(urlc)
});
p.then(function(person) {
  people.push(person);
});

$q.all将构造一个新的承诺,当所有输入承诺解析时,该承诺将得到解析。 由于我们输入了一个地图,$q.all将使用具有相同键的地图进行解析。 这些值是相应承诺的决议。 在这种情况下,这是一个人,所以我们可以直接将其推送到 People 数组中。

这是一个有点幼稚的实现。 由于调用是异步的,因此不能保证名称的顺序将保留在人员中 - 但如果它很重要,您应该不会太难解决这个问题。

创建计数

和技能的所有承诺的数组。已将这些请求分解为自己的函数以提高可读性

此外,$http then()返回一个具有属性data的 promise 对象

var getPeople = function() {    
  return $http.get('/getnames').then(function(response) {
    return response.data;
  });    
};

getPeople().then(function(people) {
  var promises = [];
  people.forEach(function(person) {
    // push new requests to promise array
    promises.push(getSkills(person));
    promises.push(getCount(person))
  });
  // when all promises resolved return `people`
  return $q.all(promises).then(function() {
     // return `people` to next `then()`
    return people;
  });
}).then(function(people) {
  console.log(people);
  $scope.people = people;
}).catch(function(){
   // do something if anything gets rejected
});
function getCount(person) {
  var urlc = '/getcountbyname/' + person.name;    
  return $http.get(urlc).then(function(response) {
    person.count = response.data
  });
}
function getSkills(person) {
  var urls = '/getskillsbyname/' + person.name;
  return $http.get(urls).then(function(response) {
    person.skills = response.data
  });
}