pos*_*spi 10 javascript ajax xmlhttprequest internals
所以我很好奇在中止异步javascript请求时发生的实际底层行为.这个问题有一些相关的信息,但我还没有找到任何全面的信息.
我的假设一直是,中止请求会导致浏览器关闭连接并完全停止处理,从而导致服务器执行相同操作(如果已设置).但我想,在这里我可能没有想到特定于浏览器的怪癖或边缘情况.
我的理解如下,我希望有人可以在必要时纠正它,这对其他人来说是一个很好的参考.
ignore_user_abort()
已被调用.因此,关闭XHR连接也可以节省服务器功耗.Tra*_*man 18
对于客户来说,最好看的地方是在源头,所以让我们这样做!:)
让我们来看看眨眼的执行XMLHttpRequest的abort
方法(线1083至1119年在XMLHttpRequest.cpp):
void XMLHttpRequest::abort()
{
WTF_LOG(Network, "XMLHttpRequest %p abort()", this);
// internalAbort() clears |m_loader|. Compute |sendFlag| now.
//
// |sendFlag| corresponds to "the send() flag" defined in the XHR spec.
//
// |sendFlag| is only set when we have an active, asynchronous loader.
// Don't use it as "the send() flag" when the XHR is in sync mode.
bool sendFlag = m_loader;
// internalAbort() clears the response. Save the data needed for
// dispatching ProgressEvents.
long long expectedLength = m_response.expectedContentLength();
long long receivedLength = m_receivedLength;
if (!internalAbort())
return;
// The script never gets any chance to call abort() on a sync XHR between
// send() call and transition to the DONE state. It's because a sync XHR
// doesn't dispatch any event between them. So, if |m_async| is false, we
// can skip the "request error steps" (defined in the XHR spec) without any
// state check.
//
// FIXME: It's possible open() is invoked in internalAbort() and |m_async|
// becomes true by that. We should implement more reliable treatment for
// nested method invocations at some point.
if (m_async) {
if ((m_state == OPENED && sendFlag) || m_state == HEADERS_RECEIVED || m_state == LOADING) {
ASSERT(!m_loader);
handleRequestError(0, EventTypeNames::abort, receivedLength, expectedLength);
}
}
m_state = UNSENT;
}
Run Code Online (Sandbox Code Playgroud)
因此,看起来大部分的grunt工作都是在内部完成的internalAbort
,看起来像这样:
bool XMLHttpRequest::internalAbort()
{
m_error = true;
if (m_responseDocumentParser && !m_responseDocumentParser->isStopped())
m_responseDocumentParser->stopParsing();
clearVariablesForLoading();
InspectorInstrumentation::didFailXHRLoading(executionContext(), this, this);
if (m_responseLegacyStream && m_state != DONE)
m_responseLegacyStream->abort();
if (m_responseStream) {
// When the stream is already closed (including canceled from the
// user), |error| does nothing.
// FIXME: Create a more specific error.
m_responseStream->error(DOMException::create(!m_async && m_exceptionCode ? m_exceptionCode : AbortError, "XMLHttpRequest::abort"));
}
clearResponse();
clearRequest();
if (!m_loader)
return true;
// Cancelling the ThreadableLoader m_loader may result in calling
// window.onload synchronously. If such an onload handler contains open()
// call on the same XMLHttpRequest object, reentry happens.
//
// If, window.onload contains open() and send(), m_loader will be set to
// non 0 value. So, we cannot continue the outer open(). In such case,
// just abort the outer open() by returning false.
RefPtr<ThreadableLoader> loader = m_loader.release();
loader->cancel();
// If abort() called internalAbort() and a nested open() ended up
// clearing the error flag, but didn't send(), make sure the error
// flag is still set.
bool newLoadStarted = m_loader;
if (!newLoadStarted)
m_error = true;
return !newLoadStarted;
}
Run Code Online (Sandbox Code Playgroud)
我不是C++专家,但从它的外观来看,internalAbort
做了一些事情:
经过大量的挖掘后,我在HttpResponseBodyDrainer(第110-124行)中遇到了一个有趣的函数,调用Finish
它看起来像是在取消请求时最终会被调用的东西:
void HttpResponseBodyDrainer::Finish(int result) {
DCHECK_NE(ERR_IO_PENDING, result);
if (session_)
session_->RemoveResponseDrainer(this);
if (result < 0) {
stream_->Close(true /* no keep-alive */);
} else {
DCHECK_EQ(OK, result);
stream_->Close(false /* keep-alive */);
}
delete this;
}
Run Code Online (Sandbox Code Playgroud)
事实证明stream_->Close
,至少在BasicHttpStream中,委托给HttpStreamParser::Close
,当给出一个non-reusable
标志(当请求被中止时似乎确实发生了,如图所示HttpResponseDrainer
)时,会关闭套接字:
void HttpStreamParser::Close(bool not_reusable) {
if (not_reusable && connection_->socket())
connection_->socket()->Disconnect();
connection_->Reset();
}
Run Code Online (Sandbox Code Playgroud)
所以,就客户端发生的情况而言,至少在Chrome的情况下,看起来你的初始直觉是正确的,据我所知:)似乎大多数怪癖和边缘情况都与调度/事件通知/线程问题,以及特定于浏览器的处理,例如将中止的XHR报告给devtools控制台.
就服务器而言,在NodeJS的情况下,您需要监听http响应对象上的"close"事件.这是一个简单的例子:
'use strict';
var http = require('http');
var server = http.createServer(function(req, res) {
res.on('close', console.error.bind(console, 'Connection terminated before response could be sent!'));
setTimeout(res.end.bind(res, 'yo'), 2000);
});
server.listen(8080);
Run Code Online (Sandbox Code Playgroud)
尝试运行它并在完成之前取消请求.您将在控制台上看到错误.
希望你发现这很有用.挖掘Chromium/Blink源非常有趣:)
归档时间: |
|
查看次数: |
1329 次 |
最近记录: |