所以我很好奇在中止异步javascript请求时发生的实际底层行为。 <{3}}中有一些相关信息,但我还没有找到任何全面的信息。
我的假设一直是中止请求会导致浏览器关闭连接并完全停止处理,从而导致服务器在设置完成后也这样做。但我想,在这里我可能没有考虑特定于浏览器的怪癖或边缘情况。
我的理解如下,我希望有人可以在必要时纠正它,这对其他人来说是一个很好的参考。
ignore_user_abort()
。因此,关闭XHR连接也可以节省服务器电源。答案 0 :(得分:18)
对于客户来说,最好看的地方是在源头,所以让我们这样做! :)
让我们看看Blink的XMLHttpRequest var s = format("$1, $2, $3", ["$2", "two", "three"]);
// returns: "two, two, three",
// needed: "$2, two, three"
方法的实现(XMLHttpRequest.cpp中的第1083-1119行):
format
因此,看起来大部分咕噜声工作都是在abort
内完成的,如下所示:
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;
}
我不是C ++专家,但从它的外观来看,internalAbort
做了一些事情:
在进行了大量的挖掘后,我在HttpResponseBodyDrainer(第110-124行)中遇到了一个名为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;
}
的有趣函数,对我来说这看起来像是在请求时最终会调用的函数取消:
internalAbort
事实证明Finish
,至少在BasicHttpStream中,委托给HttpStreamParser 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;
}
,当给出stream_->Close
标志时(似乎确实发生了)当请求被中止时,如::Close
中所示),关闭套接字:
non-reusable
所以,就客户端上发生的事情而言,至少在Chrome的情况下,看起来你的初始直觉是正确的,据我所知:)似乎大部分的怪癖和边缘情况与调度/事件通知/线程问题以及特定于浏览器的处理有关,例如将流产的XHR报告给devtools控制台。
就服务器而言,在NodeJS的情况下,您希望在http响应对象上侦听'close' event。这是一个简单的例子:
HttpResponseDrainer
尝试运行该请求并在请求完成之前取消该请求。您将在控制台上看到错误。
希望你发现这很有用。挖掘Chromium / Blink源非常有趣:)