我有一个将加载大量用户(需要一段时间)并将它们存储在原子中的函数.我想知道将用户加载到let绑定然后重置原子或者只是在原子重置中加载它们之间是否有任何区别!功能?
(let [all-users (get-users)]
(reset! users all-users))
Run Code Online (Sandbox Code Playgroud)
要么
(reset! users (get-users))
Run Code Online (Sandbox Code Playgroud)
由于reset!
是一个函数,调用(reset! users (get-users))
将表现为Clojure中的任何其他函数调用:调用中的每个S表达式将被计算,然后作为参数传递给函数.这意味着(get-users)
将首先进行评估,并将结果传递给reset!
.因此,这将与let
表单完全相同.
swap!
这些问题发生在哪里swap!
.因为您发送swap!
要在事务内部调用的函数,所以您可以更好地控制长期运行的作业是在事务内部还是外部发生.例如,如果你有函数poll-users-updates
和update-users-from-poll
,你可以设置呼叫转移到第一个功能发生的内部或外部的交易:
; outside the transaction
(swap! users update-users-from-poll (poll-users-updates))
; inside the transaction
(swap! users (fn [users] (update-users-from-poll users (poll-users-updates))))
Run Code Online (Sandbox Code Playgroud)
这里的第二种形式更有可能必须重新启动,因为更新函数运行需要更长的时间,为原子的其他一些写入留出更多时间来强制重启.
相反,第一种形式不太可能强制重试,因此通常是优选的.另一方面,如果你的poll-users-updates
函数还需要对users
数据的当前状态进行操作(例如,为了找到最近更新的用户的时间戳,为了更有效地进行轮询),那么第二种方法可能是首选,因为它可以确保您users
在进行投票时具有最新的价值.
这与STM相关的是,您的更新功能可能会被多次调用.说副作用函数是" 原子内部的危险 "可能有点强烈.虽然它们可能很危险,但最好假设它们是危险的.即使它们不是(例如当效果是幂等的时候),意思是你得到的东西叫做一次,就像你多次调用一样),最好让它们没有副作用.Clojure的refs和原子都是如此,它们在冲突的情况下重试.在contast中,代理没有重试语义,因此可以在发送给代理的函数中产生副作用.由于代理将更新函数排队并按顺序运行它们,因此不会发生冲突,因此无需重试.