javascript websockets - 控制初始连接/ onOpen何时绑定

dbe*_*ham 28 javascript html5 websocket

两个相关的问题可能更多地源于我对浏览器预解析javascript的方式缺乏了解:

var ws = new WebSocket("ws://ws.my.url.com");
ws.onOpen = function() { ... };
Run Code Online (Sandbox Code Playgroud)

似乎没有办法直接控制a的初始化WebSocket,除了将它包装在回调中,所以我假设一旦javascript代码被加载并创建到构造函数就会创建连接?

onOpen物业何时附加ws?是否存在竞争条件的可能性(如果由于某种原因,您在套接字的定义和onOpen?的定义之间有一些代码),那么onOpen在建立连接之前/之后是不可判定地绑定的(我知道您可以选择检查ws.readyState) .补充一点,是WebSocket握手阻塞吗?

我意识到这一切都是目前的草案,可能依赖于实现,我可能已经错过了一些令人眼花缭乱的显而易见的事情,但是在我的互联网搜索/浏览草案w3c规范时我看不出任何特别相关的信息,所以对我的理解有任何帮助websockets/javascript的内部工作非常感谢!

leg*_*ter 32

JavaScript是单线程的,这意味着在当前执行范围完成且网络执行有机会运行之前,无法建立网络连接.执行范围可以是当前函数(connect下例中的函数).所以,onopen如果你在使用setTimeout时很晚就绑定它,你可能会错过这个事件,例如在这个例子中你可能会错过这个事件:

查看:http://jsbin.com/ulihup/edit#javascript,html,live

码:

var ws = null;

function connect() {
  ws = new WebSocket('ws://ws.pusherapp.com:80/app/a42751cdeb5eb77a6889?client=js&version=1.10');
  setTimeout(bindEvents, 1000);
  setReadyState();
}

function bindEvents() {
  ws.onopen = function() {
    log('onopen called');
    setReadyState();
  };
}

function setReadyState() {
  log('ws.readyState: ' + ws.readyState);
}

function log(msg) {
  if(document.body) {
    var text = document.createTextNode(msg);
    document.body.appendChild(text);
  }
}

connect();
Run Code Online (Sandbox Code Playgroud)

如果您运行该示例,您可能会看到永远不会输出'onopen called'日志行.这是因为我们错过了这个活动.

但是,如果您将事件new WebSocket(...)的绑定和绑定保持onopen在相同的执行范围内,那么您将无法错过该事件.

有关scope of execution这些排队,计划和处理的更多信息以及如何排序,请查看John Resig关于JavaScript中Timers的帖子.

  • @usr API [spec states](http://dev.w3.org/html5/websockets/#handler-websocket-onopen)在"建立WebSocket连接"时应该触发`onopen`事件.根据我的回答的第一段,这只能在执行范围完成后(调用构造函数的代码)发生.如果在开发人员有机会绑定到'onopen`事件之前触发`onopen`,那将是一个根本性的缺陷. (3认同)
  • **facepalm** 认真的??**叹气** 不怀疑你,只是认为它是这样做的。为什么他们没有像几乎每个 JavaScript 库那样提供一个配置对象来创建对象...... (2认同)
  • 我只是发现在阅读答案末尾的文章之前我对 javascript 引擎缺乏一些基本的了解。 (2认同)

ipe*_*rik 5

TL;DR - 标准规定可以“在 [JS] 事件循环运行时”打开连接(例如通过浏览器的 C++ 代码),但是触发事件open必须在 JS 事件循环中排队,这意味着任何onOpen回调注册在new WebSocket(...)保证执行的同一执行块中,即使连接在当前执行块仍在执行时打开。


根据HTML 标准中的WebSocket 接口规范(重点是我的):

调用构造函数WebSocket(url, protocols)时,必须运行以下步骤:

  1. urlRecord是应用 URL 解析器的结果url
  2. 如果urlRecord失败,则抛出一个“ SyntaxErrorDOMException
  3. 如果urlRecord方案不是“ ws”或“ wss”,则抛出“ SyntaxErrorDOMException
  4. 如果urlRecord的片段非空,则抛出一个 " SyntaxError" DOMException
  5. 如果protocols是字符串,则设置protocols为仅包含该字符串的序列。
  6. 如果 中的任何值protocols出现多次或无法满足构成 Sec-WebSocket-Protocol 字段值的元素的要求(如WebSocket 协议所定义) ,则抛出“ SyntaxErrorDOMException
  7. 并行运行此步骤:

    1. 给定 urlRecord、协议和条目设置对象,建立 WebSocket 连接。[拿来]

    注意:如果建立 WebSocket 连接算法失败,则会触发失败 WebSocket 连接算法,该算法然后调用关闭 WebSocket 连接算法,该算法随后确定 WebSocket 连接已关闭,从而触发 close 事件,如下所述。

  8. 返回一个新的 WebSocket 对象,其 url 为 urlRecord。

请注意,连接的建立是“并行”运行的,并且规范进一步指出“...并行意味着这些步骤将与标准中的其他逻辑同时运行,一个接一个” (例如,该标准没有定义实现这一点的精确机制,无论是分时协作多任务、纤程、线程、进程,还是使用不同的超线程、核心、CPU、机器等。 ”

这意味着理论上可以在注册之前打开连接onOpen,即使是onOpen(...)构造函数调用之后的下一条语句。

然而......该标准在协议反馈下继续说明:

建立 WebSocket 连接后,用户代理必须将任务排队以运行以下步骤

  1. 将属性的值更改readyStateOPEN(1)。
  2. extensions如果不是该值,请将属性的值更改为正在使用的扩展null。[科进]
  3. protocol如果不是该值,请将属性的值更改为正在使用的子协议null。[科进]
  4. open触发以该对象命名的事件WebSocket

注意由于上述算法作为任务排队,因此正在建立的 WebSocket 连接和为 open 事件设置事件侦听器的脚本之间不存在竞争条件。

因此,在遵守 HTML 标准的浏览器或库中,如果注册的回调是在调用构造函数的执行块结束之前以及同一块中的任何后续语句之前注册的,则WebSocket.onOpen(...)保证执行该回调释放事件循环(例如)。await