我刚开始使用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
第一条消息有时会丢失,因为它们是在承诺通知之前发出的。仅使用EventEmitter和Callbacks不会发生此问题,因为始终会立即调用回调来阻止JavaScript线程处理传入的消息。
以下代码永远不会错过邮件:
session.on 'peer_joined', (peer) ->
peer.on 'message', (msg) ->
console.log "Message received:", msg
您是否认为我可以使用承诺解决问题?我真的想在我的抽象层中继续使用它们,以确保只接受一个对等体。还有其他方法可以避免这种竞争条件吗?
答案 0 :(得分:2)
作为通常宣传使用承诺的人,我的建议是:
承诺代表一次性事件。它们是值的抽象,一个承诺改变它的状态,它不能再被改变。承诺从挂起开始,并将状态一次更改为已完成或已拒绝。
您正面临着一个场景,您拥有许多用户,每个用户都加入并需要添加事件,用户可能会“离开”,您的场景根本不会描述相同的线性流承诺。 Promise对某种情况很有用 - 它们不适用于所有并发问题。在这里使用事件发射器是完全合适的。
您的案例(用户加入)并不真正代表已解决的代理操作。 “不会错过邮件”的代码确实更正确。
您可以做一些事情:
message
回调包装为仅在附加处理程序后触发 - 累积在创建处理程序时(在数组中)使用处理程序触发的项目,然后在添加消息处理程序时触发它们(解决后,在.then
延期退回。