JavaScript承诺和竞争条件

时间:2014-11-22 19:13:58

标签: javascript promise race-condition q

我刚开始使用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

您是否认为我可以使用承诺解决问题?我真的想在我的抽象层中继续使用它们,以确保只接受一个对等体。还有其他方法可以避免这种竞争条件吗?

1 个答案:

答案 0 :(得分:2)

作为通常宣传使用承诺的人,我的建议是:

不要在这里使用承诺

承诺代表一次性事件。它们是的抽象,一个承诺改变它的状态,它不能再被改变。承诺从挂起开始,并将状态一次更改为已完成或已拒绝。

您正面临着一个场景,您拥有许多用户,每个用户都加入并需要添加事件,用户可能会“离开”,您的场景根本不会描述相同的线性流承诺。 Promise对某种情况很有用 - 它们不适用于所有并发问题。在这里使用事件发射器是完全合适的。

您的案例(用户加入)并不真正代表已解决的代理操作。 “不会错过邮件”的代码确实更正确

如果你仍然选择在这里使用承诺

您可以做一些事情:

  • 您可以使用Q的进展事件并在创建阶段添加进度处理程序。请注意,Kris(Q的作者)称进展已被破坏,并且在下一版Q中将其删除。我建议不要使用它。
  • 您可以将message回调包装为仅在附加处理程序后触发 - 累积在创建处理程序时(在数组中)使用处理程序触发的项目,然后在添加消息处理程序时触发它们(解决后,在.then延期退回。