Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/439.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 在函数调用的哪一点上,AJAX请求实际上是由浏览器发起的?_Javascript_Ajax_Xmlhttprequest - Fatal编程技术网

Javascript 在函数调用的哪一点上,AJAX请求实际上是由浏览器发起的?

Javascript 在函数调用的哪一点上,AJAX请求实际上是由浏览器发起的?,javascript,ajax,xmlhttprequest,Javascript,Ajax,Xmlhttprequest,假设我有一个函数可以执行标准AJAX请求: function doXHR() { var xhr = new XMLHttpRequest(); xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts'); xhr.send(); xhr.onreadystatechange = () => { console.log('ready state change'); }; } doXHR(); co

假设我有一个函数可以执行标准AJAX请求:

function doXHR() {
  var xhr = new XMLHttpRequest();
  xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts');
  xhr.send();
  xhr.onreadystatechange = () => {
    console.log('ready state change');
  };
}

doXHR();
console.log('done');
这段代码将导致浏览器启动AJAX请求,但请求实际上是在函数中的哪个点启动的?根据这篇文章:

[在
send()
]之后调用
xhr.onreadystate()
是可能的,因为HTTP请求仅在当前作用域结束其任务后执行。这使程序员能够在任何位置设置回调。由于JavaScript是单线程的,因此只有当这一个线程可以自由运行新任务时,才能发送请求。HTTP请求被添加到由引擎管理的要执行的任务列表中

但当我在devtools中在send调用之后添加断点时:

我确实收到一个网络请求,尽管处于挂起状态:

在断点处,XHR的
readystate
为1,即
XMLHttpRequest.OPENED
。根据MDN的文档,
XMLHttpRequest.OPENED
表示调用了
xhr.open()

但是如果我注释掉
xhr.send()
,这样只调用
.open()

然后我没有收到挂起的网络请求:

考虑到可能需要结束函数作用域才能实际发送请求,我将断点移动到函数调用之后(并稍微修改了代码以查看XHR readyState):

但我仍然收到挂起的网络请求:

似乎调试器不仅冻结代码执行,而且冻结任何网络请求。这是有道理的,因为您不希望在断点上完成网络请求,但这也使得无法判断网络请求实际何时启动


我的问题是,请求实际上是在什么时候发出的?调用
xhr.send()?
xhr.send()
是否在函数作用域结束前立即启动请求?或者这里发生了什么事?

send
立即启动请求。这一点至少可以从以下几个方面得到暗示:

你链接到的博客的作者是正确的,没有种族条件本身,但这并不能阻止你让事情发生混乱。例如,如果您加载多个
标记,并在这些标记上设置
async=true
。在这种情况下,如果一个脚本依赖于另一个脚本,那么最终可能会出现一个不可预测的事件序列(非常类似于竞争条件),因为两个异步事件在不同的时间完成

确实,您可以在调用
send
后设置
onreadystatechange
,因为即使请求立即失败,在函数作用域完成之前,事件也不会被调度。这里的延迟不是网络请求的调度,而是表示网络请求已完成的事件的调度


需要注意的是,网络本身不是在JavaScript中处理的,而是由浏览器实现处理的,它是本机代码,可以是多线程的(尽管我不知道是否是)。这意味着浏览器完全能够在javascript运行时处理网络任务

我怀疑开发工具受到了JS的影响,所以我使用Fiddler作为代理来查看请求实际何时发出,而不是何时出现在网络请求中。事实证明,
send()
确实会立即启动网络请求。我通过在
doXHR()
函数内外运行一个非常长的
for
循环,添加了一个同步延迟,在这两种情况下,请求都会立即发出,并在
for
循环运行完之前完成。对于未来的读者:“那么为什么不在
send()之后调用
xhr.onreadystate()
有时会导致在添加处理程序之前解析请求?“即使忽略任何网络请求,即使是本地主机请求,都有足够的延迟,
onreadystate()
将在解析请求之前很久被调用,上述答案中的这句话也回答了这个问题:“在函数作用域完成之前,事件不会被调度”。这很容易测试:创建一个按钮,调用
按钮。单击()
,然后添加一个单击处理程序。单击处理程序仍然会被触发。