【问题标题】:How can I create an Angular factory with a getter and setter method without running into a race condition如何使用 getter 和 setter 方法创建 Angular 工厂而不会遇到竞争条件
【发布时间】:2016-01-12 07:32:42
【问题描述】:

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

这是工厂本身

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()

我遇到的问题是,在工厂返回空集后,无法识别工厂的进一步更改(即使经过一段时间后它确实从 API 获得了正确的响应)并且$scope.messages 仍然为空。

我已尝试将 API 调用移至 get 方法(这没有奏效,因为 get 方法通常无法及时获得 userId)并且我找不到使用承诺的方法强制get() 等待set() 完成。

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

我对 Angular 还很陌生,所以我确信有一些非常明显的东西,但现在我迷路了。谢谢。

【问题讨论】:

  • userId 在哪里?为什么你拿不到?
  • 啊抱歉我应该把视图放在那里(现在编辑它。)获取 userId 没问题 - 它只是通过一个函数传入并每次都正确登录到控制台
  • 如果你的 'get' 方法返回了 Promise,那么你可以在控制器中执行 'then' 而不必担心任何竞争条件。
  • 我不太明白这个问题很好。你能解释一下为什么比赛会出现问题吗?你能告诉我们你在哪里以及如何使用 get 函数。
  • 你可以看看github.com/Yannic92/shoppingList/blob/master/src/main/resources/…。我在这里也是这样做的

标签: javascript angularjs restangular


【解决方案1】:

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

解决方案是从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
 }

});

在您的控制器中,chain 来自 promise。

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

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

【讨论】:

【解决方案2】:

当您重新分配原始 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 

【讨论】:

  • 啊,你说得对 - 谢谢!但是除非我做错了其他事情,否则我认为我仍然遇到竞争条件问题,因为这是在调用完成之前返回空数组(如果我记录它,我什至可以在不久之后看到正确的数组。 ) 我错过了什么吗?
  • 是的,它最初会返回空数组,因为 Restangular 是异步的,并且在完成之前不会填充数组。 $resource 使用相同的概念,立即返回一个空数组。大多数人会从服务返回承诺并在承诺解析回调中更新控制器模型
  • 哦,当然 - 我发现在它填充以下页面后没有更新。有什么我应该在那里看的吗?
  • following page?不知道这意味着什么
  • mh 我不确定 concat 是如何工作的,但我现在要做的是:messages.splice(0,messages.length);消息.push.apply(消息,文档);这应该工作
【解决方案3】:

cherlietfl 是对的。

问题在于您破坏了对消息数组的引用,因为您在 get 函数中为消息分配了一个新数组。但是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
});

【讨论】:

  • 恐怕这对我不起作用,因为 get() 方法仍然返回一个空集。该数组最终确实解析正确,但到那时 return 已经被调用,并且工厂中的更新数据没有进入以下视图。我很可能在做一些愚蠢的事情,但这就是我害怕的地方
  • 您能否提供使用 GetMessages.get() 返回的消息数组的模板和该模板的控制器?
【解决方案4】:

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

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

查看:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-02
    • 2022-07-21
    相关资源
    最近更新 更多