Javascript 在函数调用的哪一点上,AJAX请求实际上是由浏览器发起的?
假设我有一个函数可以执行标准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
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()
将在解析请求之前很久被调用,上述答案中的这句话也回答了这个问题:“在函数作用域完成之前,事件不会被调度”。这很容易测试:创建一个按钮,调用按钮。单击()
,然后添加一个单击处理程序。单击处理程序仍然会被触发。