【问题标题】:XMLHttpRequest onreadystatechange never fired for readyState 0XMLHttpRequest onreadystatechange 从未为 readyState 0 触发
【发布时间】:2018-01-19 22:15:49
【问题描述】:

我设置了通常的 XMLHttpRequest,到目前为止一切正常,我可以检索数据和 readyState 更改并执行各种操作。

但没有发生的一件事是,readyState 在达到 0 时会触发 onreadystatechange() 函数。

现在我想知道...为什么?

我查看了XMLHttpRequest MDN entry,但没有提到为什么当 readyState 达到 0 时不应触发 onreadystatechange。我也没有找到关于该主题的其他信息...

片段:

// Inside a class, this = class instance
this.socket = new XMLHttpRequest();

// For easy access of this class instance in onreadystatechange
var client = this;

// Write current readyState to console
this.socket.onreadystatechange = function ()
{
    // Adding this just to verify there are no errors in if block
    console.log("readyState: "+client.socket.readyState);

    if(client.socket.readyState === 0)
    {
        console.log("readyState is 0");
    }
    else
    {
        console.log("readyState is "+client.socket.readyState);
    }
}

当 XMLHttpRequest 套接字处理完请求后,XMLHttpRequest 实例将具有值 readyState===0,至少 Firefox 的 JS 控制台告诉我。

发出请求后,JS 控制台正确列出了 readyStates 1-4 的日志消息,但没有列出 readyState 0 的消息,不是在请求之前,也不是在请求之后。

理论上,当readyState从4变回0时,不应该触发onreadystatechange()函数吗?在 macOS 上使用当前 FF 和 Safari 以及在 Win 上使用 IE11 进行了检查,到处都是相同的行为,onreadystatechange() 永远不会为 readyState 0 触发。

XMLHttpRequest 是故意不这样做,还是我做错了什么?感谢任何有用的输入。 :)

(请不要“使用 jQuery”cmets 或类似的东西,谢谢。)

更新:

在成功或失败的请求完成后,readyState 永远保持在 4,并在 FF、IE、Safari 中验证。

但是,如果正在进行的请求被中止,readyState 将从 > 0 返回到 0。所以 readystatechange 确实发生了,但是 onreadystatechange 没有被触发,在 FF 和 Safari 中验证了这一点。

更新 3:

根据XMLHttpRequest spec,当XHR 处于readyState 3(接收)并中止时,XHR 应通过onreadystatechange 事件将readyState 设置为4(完成),发出网络错误,然后将readyState 设置为0(未发送) 没有 onreadystatechange 事件。

但是当我中止处于 readyState 3(接收)的 XHR 时,它会首先使用 onreadystatechange 事件和 HTTP 状态 200(OK)将 readyState 设置为 4(完成),然后它会触发 onabort 和 onloadend 并重置 readyState为 0(未发送)...但绝不会触发 onerror 事件。

非常混乱。

【问题讨论】:

  • @Lukasas:=== 是有效的 JavaScript。这是非强制等于运算符。
  • 是的,我的错,当我看到那个运算符时,我正在严格地考虑 php。 readyState 不应为 0,因为这意味着它未设置。所以当socket准备好时,说明它已经打开了,所以应该设置为非零状态
  • 但是当我 .open() 套接字和 .send() 请求时,一旦 .readyState 达到 4 并且 .status === 200(表示一切顺利),套接字就会回到 .readyState 0. Firefox 的 JS 控制台在完成请求并返回“idling”后非常清楚地将其显示为它的 .readyState 值。 readyState 从 4 变为 0,但没有触发 onreadystatechange。
  • @Rob: "...套接字回到 .readyState 0。" 不适用于我,不适用于 Firefox,不适用于 Chrome。正如我在回答中所说,我怀疑观察错误。如果你没有找到它,你可能会发布一个 minimal reproducible example 来展示你是如何看待它的,但是对于 Firefox 将 readyState 从 4 更改回 0 来说没有意义,它不适合我,我怀疑它不会,期间。 :-)

标签: javascript ajax xmlhttprequest readystate onreadystatechange


【解决方案1】:

理论上,当readyState从4变回0时,不应该触发onreadystatechange()函数吗?

XHR 实例不可重用。 如果 Firefox 的 XHR 对象将状态从 4 带回 0 (它不适合我,见下文),这是 Firefox 特有的东西,但它没有用,并且它不会在其上触发无用的事件。

状态从DONE (4) 变为UNSENT (0) 是没有意义的。请求没有未发送。 完成。

请在评论中回复您的注释:

但是在中止之后,它返回到 0 --- 在它已经处于 readyState 3 并检索文件之后......

啊,好吧,那会有所不同——从 3 到 0,而不是从 4 到 0。如果我在 Chrome 和 Firefox 中都这样做,我也会看到。 p>

The spec for XHR's abort 说:

  1. 当你说 (readyState 3) 中止时,readyState 应该不变回 0;相反,它应该更改为 4,将响应设置为网络错误,并触发 readystatechange 事件。

  2. 如果在 readyState 为 4 时中止,它应该变回 0 - 但规范确实不说它应该触发 readystatechange 事件(而它确实说其他各种算法改变状态的次数)。

因此,在这种极端情况下,Chrome 和 Firefox 似乎都没有严格遵循规范。

【讨论】:

  • 不完全。我的“错误”是启动一个下载大文件的请求,然后使用 .abort() 停止它。在成功或失败的 .readyState===4 上,套接字永远保持在 readystate 4。但是在中止之后,它返回到 0 --- 在它已经处于 readyState 3 并检索文件之后。 Safari 也是如此。
  • @Rob:啊,但这与从 4 回到 0 不同。:-) 我在查看规范后更新了答案。有趣的是,Firefox 或 Chrome 似乎都没有遵循规范的行为。
  • 感谢您提供该规范的链接,由于各种原因,我曾多次访问该规范,但从未想过要查看该规范。但是......规范说“如果状态已完成,则将状态设置为未发送并响应网络错误。” ......但是......“状态完成”意味着readyState 4,对吗?绝对不是正在进行的readyState 3。令人困惑的是,我最初是通过谷歌找到这本书的摘录,与规范所说的相比,这有点误导:books.google.de/… 任何地方都没有提到“no onreadystatechange”。
  • @Rob:对。我的意思是,如果您在状态 3 时中止请求,规范说它应该更改为状态 4,将响应设置为网络错误,然后触发事件。但是如果你在它已经是状态 4 时中止,它应该变回 0 并且 not 触发事件。 (我不明白为什么,但这就是它所说的。)
  • 啊哈,现在我明白了。如果在 3 处中止,则设置为 4,触发错误和 readystatechange,然后设置为 0 而没有 readystatechange。如果在 4 时中止,只需设置为 0 并且不触发 readystatechange。到此为止,就这么明白了。但是当我的 XHR 处于 3 / 接收并且我中止时,它会将 readyState 设置为 4,HTTP 状态为 200(OK),然后首先触发 onabort,然后触发 onloadend 事件。任何地方都不会调用 onerror...
【解决方案2】:

0 是表示请求尚未发送的值。您无法取消发送请求,因此事件无法从 4 触发到 0。 这就像您发送了一封邮件,然后等待您发送它。那没有意义。您只能等待其他事情发生(例如,邮递员拿走了它,它到达了......)但显然,如果您等待已经发生的事情,您将等待很长时间。

值状态描述

0 UNSENT 客户端已创建。 open() 尚未调用。

1 OPENED open() 已被调用。

2 HEADERS_RECEIVED send() 已被调用,并且标头和状态为 可用。

3 LOADING 下载; responseText 保存部分数据。

4 DONE 操作完成。

更新

在寻找答案时,我发现了XMLHttpRequest 的这个实现。这表明 abort 函数没有调度任何事件。这可能就是您无法捕获该事件的原因,因为它根本没有被触发。

cXMLHttpRequest.prototype.abort = function() {
        // Add method sniffer
        if (cXMLHttpRequest.onabort) {
            cXMLHttpRequest.onabort.apply(this, arguments);
        }

        // BUGFIX: Gecko - unnecessary DONE when aborting
        if (this.readyState > cXMLHttpRequest.UNSENT) {
            this._aborted = true;
        }

        this._object.abort();

        // BUGFIX: IE - memory leak
        fCleanTransport(this);

        this.readyState = cXMLHttpRequest.UNSENT;

        delete this._data;

        /* if (this._async) {
        *   fQueue_remove(this);
        * }
        */
    };

【讨论】:

  • 您必须阅读整个问题。 :-) OP 说,在 Firefox 中请求完成后,他看到它从 4 回到 0,但没有看到相应的事件。如果这是真的,那将是一个变化。
  • 为了完整起见,在这里我还要提一下,我更新了原帖。对于已经达到 readyState 4 的成功或失败事务,readyState 永远保持在 4。但是对于已经处于 readyState 3 然后在检索时取消的正在进行的事务,readyState 确实返回到 0。在我看来,这是一个变化,但仍然没有触发 onreadystatechange。
  • 嗯...这确实改变了事情,我没有看到。在那种情况下,确实应该有 readyState 变化
  • @AlexandreThyvador 感谢您的调查。 :) Crowder 在他的回复中已经向我指出了 XMLHttpRequest 规范,其中提到在调用 .abort() 时不会触发任何 onreadystatechange 事件。但是,规范说,如果请求已经“完成”(这对我来说意味着 readyState 4),readyState 只会重置为 0,但它会在 readyState 3(接收)期间这样做,而 not 当已经在 readyState 4... 非常混乱。 :)
【解决方案3】:

默认性质

Readystate 0 属于未发送状态。这是创建 xhr 对象后的默认进入状态。此时 eventhandler 'onreadystatechange' 没有被调用,因为它被设计为只调用从 0 到某事的状态变化。

我收到了你的案子。现在我要回答你的问题 **为什么当状态从 4 变为 0 时不会触发“onreadystatechange”?

     var xhr = new XMLHttpRequest();
 //here xhr.readystate is 0
 xhr.onreadystatechange = () => {
     console.log('state', xhr.readyState)
     //**session one** 
     // console will print 1 when request opened
     // 2 when sent and response header received
     // 3 when body is receiving 
     // 4 when done
     //**session two**
     // prints 1 
     //note:readystate comes to 1 directly from 4 not 0.The state 0 is 
     //only available upto very first time you called open
 }

 //session one
 xhr.open('GET', '/api', true);
 xhr.send()

 //session two
 //here xhr.readystate is 4
 xhr.open('GET', '/api', true);
 //here xhr.readystate is 1 there is no 0 in middle.
 xhr.send();

【讨论】:

  • 感谢您的回复。 T.J. Crowder 已经将我指向XMLHttpRequest spec,其中提到不会触发任何 onreadystatechange 事件,因此部分谜团已解开。但是,规范还提到,当调用 .abort() 时,“如果状态已完成”,readyState 将重置为 0,这对我来说意味着“如果 readyState 已经为 4”。但是,如果 readyState 只有 3,.abort() 会将 readyState 重置为 0,这正在进行中并且 not(据我所知)“完成”了。很奇怪。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-06-21
  • 2015-10-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-11
  • 1970-01-01
相关资源
最近更新 更多