30

最近我和一些同事正在讨论 AngularJS 服务是否应该有状态。我们提出了一些支持和反对它的论点,我想就这个主题获得更多的想法和反馈。在我的搜索中,我发现了这一点,但似乎没有提到任何明确的最佳实践。在无客户端的世界中,服务永远不应该保持状态,但我开始认为它可能是可接受的客户端,因为它是一个不同的问题。

服务保持状态的原因:

  1. 该服务不会被多个线程访问。每个浏览器都有自己的服务实例。
  2. 允许服务仅保存它关心的状态,而不是将其存储在 rootScope 中。封装

服务不保持状态的原因:

  1. 服务不再是幂等的。调用函数可能会改变状态,因此在根据服务的状态调用它时可能会产生不同的结果。
  2. 我认为总的来说这将更容易测试。

一种可能解决“用于保持状态的服务”部分中的#2 的方法是在包含应用程序当前状态的 rootScope 上设置一个 appState 对象。然后所有的状态将被收集在一个位置,然后你只需在你的服务中提取你需要的东西。我发现了这个并想知道

4

4 回答 4

17

这可能取决于您所说的“状态”是什么意思,但在许多情况下,我认为答案是肯定的:服务应该保持状态。

例如,如果您有一个负责与 API 通信的服务,则该服务可以保持身份验证状态。

顺便说一句,我不确定 AngularJS 服务的幂等性有多重要——它们是单例的,因此天生就有一些状态。您可以(并且在某些情况下必须)在服务上创建幂等方法,但这是一个单独的问题。

于 2013-01-09T16:44:45.853 回答
11

在 AngularJS 中,服务是通过工厂函数传入的。基本上它们是可以包含某些状态的对象(例如,用于缓存或存储执行其操作所需的数据)。

一种可以同时考虑具有/不具有状态的缺点的一个好的解决方案是,当服务(实际上可能是函数)返回包含状态的对象时。

看一下$http服务:你可以得到这个服务调用的实例

var x = $http({url:'...'});

然后打电话给

var result = x.get() //actually `$http.get` is shortcut of this operation

ngResource: 使用服务相同,您可以获得具有某些可以执行所需操作的状态的对象。

所以基本上我认为这是最好的选择:从某一点上,您可以通过将可以通过操作修改的状态移动到单独的对象中来避免“副作用”,而不是存储在服务本身中,但可以在该对象中具有特定状态以便能够存储自定义信息(如身份验证信息等)。

于 2013-01-09T18:02:42.253 回答
2

IMO 是的,服务可以有状态。我说“可以”作为服务可以被认为类似于经典的非客户端服务 - 提供者,但它也可以意味着完全不同的东西,在 angularJS 中。例如,作为应用程序中的 rootScope-y 单实例元素,它可以单独用于管理状态。就我而言,这使我能够确保许多应用程序的状态结构相同,即使在引导过程中为每个应用程序定义了它们的单独状态结构,但当该模块发生更改时,会话状态之类的东西总是相同并更新。

于 2013-01-09T17:42:13.043 回答
-5

服务不应该有状态的原因是当你有多个线程访问服务时它会导致竞争条件。

服务中状态的一个常见问题是:

  1. 线程 1 写入状态
  2. 线程 2 写入状态
  3. 线程 1 从状态读取
  4. 线程 2 从状态读取

线程 1 现在有错误的值。

话虽这么说,javascript 目前是单线程的,所以你不会遇到这样的线程访问问题。但是,如果我有一个服务,其中多个异步 $http 调用都写入同一个服务变量,我会担心。如果只有这样我晚上能睡得更好,我会尝试编写我所有的服务方法,以便它们成为实际数据的传递。

相反,您可以考虑将状态尽可能多地放在后端,而不是在服务中维护状态。可以维护和查询诸如“经过身份验证”甚至宽度和高度之类的内容。这可以打开一些可能性,允许用户离开应用程序,返回并找到他们的所有偏好设置并登录。您可以在 cookie 中存储会话 ID 并将所有这些内容保存在后端。

如果您确实采用了具有单独对象来存储状态的路线,那么当状态中的某些内容发生更改时,您可能可以从服务中向它发送 $emit:如何从工厂发出事件。这将产生一个很好的副作用,即让多个服务能够修改一个统一的应用程序状态,因为状态没有存储在任何一个服务中(或者更糟糕的是分散在多个服务中)。

于 2014-01-07T04:42:58.767 回答