如何在没有 运行 竞争条件的情况下使用 getter 和 setter 方法创建 Angular 工厂

How can I create an Angular factory with a getter and setter method without running into a race condition

我正在创建一个工厂以从一个页面获取 userId,调用 REST API,然后 return 以下视图中的结果。我最初的尝试主要来自 this answer,但不出所料,我一直陷入无法及时响应且 get() 方法 return 是一个空数组的情况。

这是工厂本身

app.factory('GetMessages', function() {
 var messages = []

 function set(userId) {

   Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
    messages = docs   
    })
 }

 function get() {
    return messages;
 }

 return {
  set: set,
  get: get
 }

});

值得一提的是,我毫不费力地将 userId 导入工厂,因为它只是通过这样的函数传入的

查看:

<a ng-click='passToFactory(message.user.id)' href='/home/inbox/reply'>Reply</a>

控制器:

$scope.passToFactory = function(id) {
    GetMessages.set(id);
};

下面视图的控制器只是 $scope.messages = GetMessages.get()

我遇到的问题是,在工厂 returns 空集之后,没有识别出工厂的进一步更改(即使在时间过去后它确实从 API) 和 $scope.messages 保持为空。

我试图将 API 调用移动到 get 方法(这没有用,因为 get 方法通常不能及时获得 userId),但我做不到找到一种使用承诺强制 get() 等待 set() 完成的方法。

我更愿意在最终解决方案中继续使用 Restangular,但这是一件花费太多时间的小事,因此任何修复都有效。

我是 Angular 的新手,所以我确信有一些非常明显的东西,但现在我只是迷路了。谢谢。

您在重新分配时破坏了对原始 messages 数组的引用。

尝试:

Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
    messages.concat(docs) ; // keep same array reference   
});

解释为什么它不起作用的简单示例

var arr = [];
var x   = arr;
arr     = [1,2,3]; // is now a different array reference
console.log(x); // is still empty array. x !== arr now 

cherlietfl 是对的。

问题是您破坏了对 messages 数组的引用,因为您在 get 函数中为 messages 分配了一个新数组。但是 concat 也在这样做。

试试这个:

Restangular.all('/api/messages/').getList({'_id': userId}).then(function(docs){
  messages.splice(0, messages.length); // clear the array
  messages.push.apply(messages, docs); //add the new content
});

尝试将您的功能分配给范围。然后在模型中调用该函数。像这样:

// controller
$scope.getMessages = GetMessages.get;

查看:

<div ng-repeat="message in getMessages()"></div>

这样当请求调用完成并且摘要循环再次通过观察者时,将调用 get 函数,您将收到消息。

您遇到的竞争条件是 .then 方法内的函数在调用 set 函数后 异步执行。如果 get 函数在 之前 执行 $q 服务履行承诺,则 get 函数 returns 一个空数组。

解决方案是从 promise 中保存 promisechain

app.factory('GetMessages', function() {

 var promise;

 function set(userId) {

     promise = Restangular.all('/api/messages/').getList({'_id': userId});
 }

 function get() {
    return promise;
 }

 return {
  set: set,
  get: get
 }

});

在您的控制器中, 来自承诺。

GetMessages.get.then( function (docs) {
    $scope.messages = docs;
}) .catch ( function (error) {
    //log error
};

有关链接 承诺的更多信息,请参阅AngularJS $q Service API Reference -- chaining promises