我刚开始使用 Promises在 JavaScript 中使用 Q library .我遇到了竞争条件,想知道解决它的最佳方法是什么。
问题是 Q 总是使用 process.nextTick()
(在 Promises/A+ 规范中也提到)调用回调,这意味着我可能会错过当时对象的一些状态变化在 promise 的解决和回调被调用的时间之间。我的具体问题是我有一个传入连接并且丢失了第一条消息。使用 EventEmitter
分发消息。
代码看起来像这样。它使用 palava-client (但问题应该是普遍的)并且是用 CoffeeScript 写的:
defer = q.defer()
session.on 'peer_joined', (peer) ->
defer.resolve(peer)
return defer.promise
还有别的地方
peer_promise.then (peer) ->
peer.on 'message', (msg) ->
console.log "Message received:", msg
第一条消息有时会丢失,因为它们在 promise 收到通知之前就已发出。仅使用 EventEmitter 和回调不会出现此问题,因为总是立即调用回调,阻止 JavaScript 线程处理传入消息。
以下代码绝不会丢失消息:
session.on 'peer_joined', (peer) ->
peer.on 'message', (msg) ->
console.log "Message received:", msg
您认为我可以使用 promises 解决问题吗?我真的很想在我的抽象层中继续使用它们,以确保只接受一个对等方。有没有其他方法可以避免这种竞争条件?
最佳答案
作为平时推广使用promise的人,我的建议是:
不要在这里使用 promise
Promises 代表一次性事件。它们是对 值 的抽象,一个 promise 改变了它的状态,它不能再被改变。一个 promise 以未决状态开始,一次状态变为已完成或已拒绝。
您面临的场景是您有很多用户,每个用户都加入并需要添加事件,用户可能会“离开”,您的场景根本无法描述 promise 所擅长的相同线性流。 Promises 对于特定场景很有用——它们并不适用于所有并发问题。在这里使用事件发射器是非常合适的。
您的情况(用户加入)并不真正代表已解决的代理操作。 “不错过消息”的代码确实更正确。
如果你还是选择在这里使用promises
您可以做一些事情:
- 您可以使用 Q 的进度事件并在创建阶段添加进度处理程序。请注意,Kris(Q 的作者)称进程已被破坏,并将在 Q 的下一版本中将其删除。我建议不要这样做。
- 您可以将
message
回调包装为仅在附加处理程序后触发 - 累积创建处理程序时触发的项目(在数组中),然后在消息处理程序时触发它们添加(在您解决后,在延迟的.then
中返回。
关于JavaScript Promise 和竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27081390/