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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8393092/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-24 05:49:16  来源:igfitidea点击:

javascript websockets - control initial connection/when does onOpen get bound

javascripthtmlwebsocket

提问by dbeacham

Two related questions that may be more rooted in my lack of knowledge of how/if browsers pre-parse javascript:

两个相关的问题可能更根源于我缺乏浏览器如何/是否预解析 javascript 的知识:

var ws = new WebSocket("ws://ws.my.url.com");
ws.onOpen = function() { ... };

There appears to be no way to directly control the initialisation of a WebSocket, beyond wrapping it in a callback, so I assume the connection is created as soon as the javascript code is loaded and get to the constructor?

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

When does the onOpenproperty get attached to ws? Is there any possibility of a race condition (if for some reason you had some code in between the definition of the socket and the definition of onOpen?) so that onOpenis undecidably bound before/after the connection is established (I know you could optionally check ws.readyState). Supplementary to this, is the WebSocket handshake blocking?

什么时候onOpen附上财产ws?是否存在竞争条件的可能性(如果出于某种原因,您在套接字的定义和 的定义之间有一些代码onOpen?),以便onOpen在建立连接之前/之后无法确定地绑定(我知道您可以选择检查ws.readyState) . 作为补充,WebSocket 握手是否阻塞?

I realise it's all a draft at the moment, possibly implementation dependent and I may have missed something blindingly obvious, but I couldn't see anything particular pertinent on my internet searches/skim through the draft w3c spec, so any help in my understanding of websockets/javascript's inner workings is very much appreciated!

我意识到目前这一切都是草案,可能取决于实现,我可能错过了一些非常明显的内容,但是我在互联网搜索/浏览 w3c 规范草案时看不到任何特别相关的内容,因此对我理解的任何帮助非常感谢 websockets/javascript 的内部工作原理!

回答by leggetter

JavaScript is single threaded which means the network connection can't be established until the current scope of execution completes and the network execution gets a chance to run. The scope of execution could be the current function (the connectfunction in the example below). So, you could miss the onopenevent if you bind to it very late on using a setTimeout e.g. in this example you can miss the event:

JavaScript 是单线程的,这意味着在当前执行范围完成并且网络执行有机会运行之前无法建立网络连接。执行范围可以是当前函数(connect下例中的函数)。因此,onopen如果您很晚才使用 setTimeout 绑定到该事件,则您可能会错过该事件,例如在此示例中,您可能会错过该事件:

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

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

Code:

代码:

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();

If you run the example you may well see that the 'onopen called' log line is never output. This is because we missed the event.

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

However, if you keep the new WebSocket(...)and the binding to the onopenevent in the same scope of execution then there's no chance you'll miss the event.

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

For more information on scope of executionand how these are queued, scheduled and processed take a look at John Resig's post on Timers in JavaScript.

有关这些信息scope of execution以及如何排队、调度和处理的更多信息,请查看 John Resig在 JavaScript 中的计时器的帖子。

回答by senz

@leggetteris right, following code did executes sequentially:

@ 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)});

})();

But, in W3C specthere is a curious line:

但是,在W3C 规范中有一条奇怪的线:

Return a new WebSocket object, and continue these steps in the background (without blocking scripts).

返回一个新的 WebSocket 对象,并在后台继续这些步骤(不阻塞脚本)。

It was confusing for me, when I was learning browser api for it. I assume that user agents ignoring it, or I misinterpreting it.

当我为它学习浏览器 api 时,这让我感到困惑。我假设用户代理忽略了它,或者我误解了它。

回答by Alon Halimi

Pay attention to the fact that I/O may occur within the scope of execution. For example, in the following code

注意执行范围内可能会发生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 );
}

, the message "Web Socket is connected"will appear or not, depending how much time it took you to close the "Hi"alert

,该消息"Web Socket is connected"是否会出现,取决于您关闭"Hi"警报所花费的时间

回答by ipetrik

TL;DR - The standard states that the connection can be opened "while the [JS] event loop is running" (e.g. by the browser's C++ code), but that firing the openevent must be queued to the JS event loop, meaning any onOpencallback registered in the same execution block as new WebSocket(...)is guaranteed to be executed, even if the connection gets opened while the current execution block is still executing.

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



According to The WebSocket Interfacespecification in the HTML Standard (emphasis mine):

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

The WebSocket(url, protocols)constructor, when invoked, must run these steps:

  1. Let urlRecordbe the result of applying the URL parser to url.
  2. If urlRecordis failure, then throw a "SyntaxError" DOMException.
  3. If urlRecord's scheme is not "ws" or "wss", then throw a "SyntaxError" DOMException.
  4. If urlRecord's fragment is non-null, then throw a "SyntaxError" DOMException.
  5. If protocolsis a string, set protocolsto a sequence consisting of just that string.
  6. If any of the values in protocolsoccur more than once or otherwise fail to match the requirements for elements that comprise the value of Sec-WebSocket-Protocol fields as defined by The WebSocket protocol, then throw a "SyntaxError" DOMException.
  7. Run this step in parallel:

    1. Establish a WebSocket connection given urlRecord, protocols, and the entry settings object. [FETCH]

    NOTEIf the establish a WebSocket connection algorithm fails, it triggers the fail the WebSocket connection algorithm, which then invokes the close the WebSocket connection algorithm, which then establishes that the WebSocket connection is closed, which fires the close event as described below.

  8. Return a new WebSocket object whose url is urlRecord.

WebSocket(url, protocols)构造函数,调用时,必须执行这些步骤:

  1. urlRecord是运用URL解析器的结果url
  2. 如果urlRecord失败,则抛出一个 " SyntaxError" DOMException
  3. 如果urlRecord方案不是“ ws”或“ wss”,则抛出“ SyntaxErrorDOMException
  4. 如果urlRecord的片段不为空,则抛出一个 " SyntaxError" DOMException
  5. 如果protocols是字符串,则设置protocols为仅由该字符串组成的序列。
  6. 如果任何一个值,在protocols出现一次以上的或以其他方式不匹配对于包括由限定仲丁基网页套接字协议字段的值元素的要求WebSocket协议,则抛出一个“ SyntaxErrorDOMException
  7. 并行运行此步骤:

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

    注意如果建立 WebSocket 连接算法失败,它会触发失败 WebSocket 连接算法,然后调用关闭 WebSocket 连接算法,然后建立 WebSocket 连接关闭,触发关闭事件,如下所述。

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

Note the establishment of the connection is run 'in parallel', and the specification further statesthat "...in parallel meansthose steps are to be run, one after another, at the same time as other logic in the standard (e.g., at the same time as the event loop). This standard does not define the precise mechanism by which this is achieved, be it time-sharing cooperative multitasking, fibers, threads, processes, using different hyperthreads, cores, CPUs, machines, etc."

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

Meaning that the connection cantheoretically be opened before onOpenregistration, even if onOpen(...)is the next statement after the constructor call.

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

However... the standard goes on to state under Feedback from the protocol:

但是......标准继续在协议反馈下声明:

When the WebSocket connection is established, the user agent must queue a task to run these steps:

  1. Change the readyStateattribute's value to OPEN(1).
  2. Change the extensionsattribute's value to the extensions in use, if it is not the nullvalue. [WSP]
  3. Change the protocolattribute's value to the subprotocol in use, if it is not the nullvalue. [WSP]
  4. Fire an event named openat the WebSocketobject.

NOTESince the algorithm above is queued as a task, there is no race condition between the WebSocket connection being established and the script setting up an event listener for the open event.

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

  1. readyState属性值更改为OPEN(1)。
  2. extensions属性的值更改为正在使用的扩展名(如果不是该null值)。[WSP]
  3. protocol属性的值更改为正在使用的子协议(如果不是该null值)。[WSP]
  4. 火命名事件openWebSocket对象。

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

So in a browser or or library that adheres to the HTML Standard, a callback registered to WebSocket.onOpen(...)is guaranteed to execute, ifit is registered before the end of the execution block in which the constructor is called, and before any subsequent statement in the same block that releases the event loop (e.g. await).

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

回答by Jacob

No actual I/O will happen until after your script finishes executing, so there should not be a race condition.

在您的脚本完成执行之前不会发生实际的 I/O,所以不应该有竞争条件。