[英]javascript websockets - control initial connection/when does onOpen get bound
两个相关的问题可能更根源于我缺乏浏览器如何/是否预解析 javascript 的知识:
var ws = new WebSocket("ws://ws.my.url.com");
ws.onOpen = function() { ... };
除了将它包装在回调中之外,似乎没有办法直接控制WebSocket
的初始化,所以我假设一旦加载 javascript 代码并到达构造函数就创建连接?
onOpen
属性何时附加到ws
? 是否存在竞争条件的可能性(如果出于某种原因,您在套接字的定义和onOpen
的定义之间有一些代码?)以便onOpen
在建立连接之前/之后不可确定地绑定(我知道您可以选择检查ws.readyState
)。 作为补充,WebSocket 握手是否阻塞?
我意识到目前这一切都是草案,可能取决于实现,我可能错过了一些非常明显的内容,但是我在互联网搜索/浏览 w3c 规范草案时看不到任何特别相关的内容,因此对我理解的任何帮助非常感谢 websockets/javascript 的内部工作原理!
JavaScript 是单线程的,这意味着在当前执行范围完成并且网络执行有机会运行之前无法建立网络连接。 执行范围可以是当前函数(下例中的connect
函数)。 因此,如果您很晚才使用 setTimeout 绑定到onopen
事件,则您可能会错过onopen
事件,例如在本例中,您可能会错过该事件:
查看: 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();
如果您运行该示例,您很可能会看到永远不会输出“onopen called”日志行。 这是因为我们错过了这次活动。
但是,如果您将new WebSocket(...)
和onopen
事件的绑定保持在同一执行范围内,那么您就不会错过该事件。
有关scope of execution
以及它们如何排队、调度和处理的更多信息,请查看 John Resig在 JavaScript 中的计时器的帖子。
@leggetter是对的,以下代码确实按顺序执行:
(function(){
ws = new WebSocket("ws://echo.websocket.org");
ws.addEventListener('open', function(e){
console.log('open', e);
ws.send('test');
});
ws.addEventListener('message', function(e){console.log('msg', e)});
})();
但是,在W3C 规范中有一条奇怪的线:
返回一个新的 WebSocket 对象,并在后台继续这些步骤(不阻塞脚本)。
当我为它学习浏览器 api 时,这让我感到困惑。 我假设用户代理忽略了它,或者我误解了它。
TL;DR - 标准规定可以“在 [JS] 事件循环运行时”打开连接(例如通过浏览器的 C++ 代码),但是触发open
事件必须排队到 JS 事件循环,这意味着任何在与new WebSocket(...)
相同的执行块中注册的onOpen
回调保证会被执行,即使在当前执行块仍在执行时连接被打开。
根据 HTML 标准中的 WebSocket 接口规范(重点是我的):
WebSocket(url, protocols)
构造函数在调用时必须运行以下步骤:
- 让
urlRecord
成为将 URL 解析器应用于url
。- 如果
urlRecord
失败,则抛出“SyntaxError
”DOMException
。- 如果
urlRecord
的方案不是“ws
”或“wss
”,则抛出“SyntaxError
”DOMException
。- 如果
urlRecord
的片段不为空,则抛出“SyntaxError
”DOMException
。- 如果
protocols
是字符串,则将protocols
设置为仅由该字符串组成的序列。- 如果在任何的值的
protocols
出现一次以上的或以其他方式不匹配对于包括由WebSocket协议所定义仲丁基网页套接字协议字段的值元素的要求,则抛出一个“SyntaxError
”DOMException
。并行运行此步骤:
- 建立一个给定 urlRecord、协议和条目设置对象的 WebSocket 连接。 [拿来]
注意如果建立 WebSocket 连接算法失败,则触发失败 WebSocket 连接算法,然后调用关闭 WebSocket 连接算法,然后建立 WebSocket 连接已关闭,如下所述触发关闭事件。
- 返回一个新的 WebSocket 对象,其 url 为 urlRecord。
请注意,连接的建立是“并行”运行的,并且规范进一步指出“......并行意味着这些步骤将一个接一个地与标准中的其他逻辑同时运行(例如,同时作为事件循环) 。该标准没有定义实现这一点的精确机制,无论是分时协作多任务、纤程、线程、进程、使用不同的超线程、内核、CPU、机器等。 ”
这意味着连接可以在理论上之前打开onOpen
注册,即使onOpen(...)
是构造函数调用后面的语句。
但是......标准继续在协议反馈下声明:
建立 WebSocket 连接后,用户代理必须将任务排队以运行以下步骤:
- 将
readyState
属性的值更改为OPEN
(1)。- 将
extensions
属性的值更改为正在使用的扩展,如果它不是null
值。 [WSP]- 将
protocol
属性的值更改为正在使用的子protocol
(如果它不是null
值)。 [WSP]- 在
WebSocket
对象上WebSocket
一个名为open
的事件。注意由于上述算法作为任务排队,因此在建立的 WebSocket 连接和为打开事件设置事件侦听器的脚本之间不存在竞争条件。
因此,在遵循 HTML 标准的浏览器或库中,如果在调用构造函数的执行块结束之前注册,则保证会执行注册到WebSocket.onOpen(...)
的回调,并且在释放事件循环的同一块中的任何后续语句之前(例如await
)。
注意执行范围内可能会发生I/O。 例如,在下面的代码中
var ws = new WebSocket("ws://localhost:8080/WebSockets/example");
alert("Hi");
ws.onopen = function(){
writeToScreen("Web Socket is connected!!" + "<br>");
};
function writeToScreen(message) {
var div = document.getElementById('test');
div.insertAdjacentHTML( 'beforeend', message );
}
, 消息"Web Socket is connected"
将出现与否,取决于您关闭"Hi"
警报所花费的时间
在您的脚本完成执行之前不会发生实际的 I/O,所以不应该有竞争条件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.