两个相关的问题可能更多地源于我对浏览器预解析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内部工作的理解!
答案 0 :(得分:30)
JavaScript是单线程的,这意味着在当前执行范围完成且网络执行有机会运行之前,无法建立网络连接。执行范围可以是当前函数(下例中的connect
函数)。所以,如果你在使用setTimeout时很晚就绑定它,你可能会错过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在Timers in JavaScript上的帖子。
答案 1 :(得分:2)
@ 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 spec中有一条奇怪的路线:
返回一个新的WebSocket对象,并在后台继续执行这些步骤(不阻止脚本)。
当我学习浏览器api时,这对我来说很困惑。我假设用户代理忽略它,或者我误解了它。
答案 2 :(得分:2)
注意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"
提醒所需的时间
答案 3 :(得分:2)
TL; DR-标准声明可以在“ [JS]事件循环运行时”(例如,通过浏览器的C ++代码)打开连接,但是必须触发open
事件被排队到JS事件循环中,这意味着即使在当前执行块仍在执行的情况下打开了连接,也保证可以执行与onOpen
在同一执行块中注册的任何new WebSocket(...)
回调。 / strong>
根据HTML标准(强调我的)中的The WebSocket Interface规范:
WebSocket(url, protocols)
构造函数在被调用时必须运行以下步骤:
- 让
urlRecord
是将URL解析器应用于url
的结果。- 如果
urlRecord
失败,则抛出“SyntaxError
”DOMException
。- 如果
urlRecord
的方案不是“ws
”或“wss
”,则抛出“SyntaxError
”DOMException
。- 如果
urlRecord
的片段不为空,则抛出一个“SyntaxError
”DOMException
。- 如果
protocols
是字符串,请将protocols
设置为仅包含该字符串的序列。- 如果
protocols
中的任何一个值出现多次或以其他方式无法满足对组成 WebSocket协议定义的Sec-WebSocket-Protocol字段值的元素的要求>,然后抛出“SyntaxError
”DOMException
。并行运行此步骤:
- 在给定urlRecord,协议和条目设置对象的情况下建立WebSocket连接。 [FETCH]
注意如果建立WebSocket连接算法失败,则会触发WebSocket连接算法失败,然后调用close WebSocket连接算法,然后确定WebSocket连接已关闭,从而触发关闭事件,如下所述。
- 返回一个新的WebSocket对象,其URL为urlRecord。
请注意,连接的建立是“并行”运行的,the specification further states表示“ ... 并行是指这些步骤要一步一步地运行,与标准中的其他逻辑同时(例如与事件循环同时)。该标准并未定义实现此目标的精确机制,例如分时协作多任务处理,纤维,线程,进程,使用不同的超线程,内核,CPU,机器等。”
这意味着理论上可以在onOpen
之前打开连接 ,即使onOpen(...)
是构造函数调用之后的下一条语句。
但是...标准继续在Feedback from the protocol下声明:
建立WebSocket连接后,用户代理必须将任务排队才能运行这些步骤:
- 将
readyState
属性的值更改为OPEN
(1)。- 如果
extensions
属性的值不是null
,请更改为正在使用的扩展名。 [WSP]- 将
protocol
属性的值更改为正在使用的子协议(如果它不是null
的值)。 [WSP]- 在
open
对象上触发名为WebSocket
的事件。注意由于上述算法作为任务排队,因此在WebSocket连接建立和脚本为open事件设置事件侦听器之间没有竞争条件。
因此,在遵循HTML标准的浏览器或库中,可以确保执行注册到WebSocket.onOpen(...)
的回调,如果,该回调是在执行块结束之前注册的。构造函数被调用,并且在同一块中释放事件循环的任何后续语句之前(例如await
)。
答案 4 :(得分:1)
在脚本完成执行之后才会发生实际的I / O,因此不应存在竞争条件。