我应该为我的ajax调用实现故障保护,还是没有必要?

时间:2015-05-16 12:27:34

标签: javascript jquery ajax

如果我知道它正确,javascript是单线程的,这意味着,同时只做一件事。我正在创建一个完整的ajax网页,其中所有导航都已完成,通过将数据发送到服务器,并在javascript的帮助下在屏幕上呈现结果。

让我们以下面的javascript:

为例
$.post( "script.php", function( data ) {
  for (var z=0; z<data; z++) {
    console.log("B");
  }
});

$.post( "script.php", function( data ) {
  for (var z=0; z<data; z++) {
    console.log("C");
  }
});

var i=-1;
while (i<0) { 
/* I know that this is an infinite cycle, but let's just bear with this for the sake of my 
example, and let's pressume the browser would run this correctly, without lagging, crashing*/
  console.log("A");
}

我们还假设script.php生成一个5-300之间的随机数,睡眠时间为秒,然后回显这个数字。浏览器将如何解释此代码?我的逻辑说明了以下事项,如果我在某处错了,请纠正我:

  1. 由于随机数,我们不知道哪个帖子会更快完成。
  2. Javascript将首先调用script.php文件,之后,它将再次调用script.php文件,然后开始将字母A记录到控制台。
  3. 当其中一个jQuery帖子完成后会发生什么?由于javascripts单线程,while循环的执行是否会停止,并且脚本开始执行该帖子的回调?当我们从第一篇文章执行回调时,如果第二篇文章完成后会发生什么?第一个帖子的回调是否停止,javascript开始执行第二个帖子回调?是否有可能在我的脚本的某个时候,我会看到以下console.log消息?

      

    A A B B B C C C C A A

    我的问题的原因是:如果用户正在浏览我的网站,点击链接,我的javascript将请求发送到服务器,但是在请求响应到来之前,用户点击了一秒钟链接?这两个答案是否可能混合在一起?或者两个回复都将被执行,但是按照他们到达的顺序?

3 个答案:

答案 0 :(得分:0)

他们绝对可能以错误的顺序到达。阻止UI触发第二个ajax调用,或者只是在第一个没有返回时才会在第二个调用启动之前终止它。

网络是一种痛苦,如果您打开chrome dev工具,您可以看到每个请求都会随着传输时间的随机波动而开始和停止。有时,第一个字节的时间会因某些奇怪的原因而爆炸。网络很奇怪。

即使加载此页面,也会有先启动并完成第二次的请求。如果用户触发了ajax调用,则需要处理它们。如果您以编程方式执行一系列ajax调用,则可以使用async等库来简化这些调用。

timeline of network requests

答案 1 :(得分:0)

你问这个问题的方式有点令人困惑,这可能解释了一些解决其他问题的长答案,而且还没有人回答你想要的问题。您的问题的关键(通过您对其他答案的后续评论证明)是关于用户点击内容链接并且内容以错误的顺序显示。最糟糕的情况是较早的请求在稍后的请求和覆盖屏幕上的内容后完成。

虽然可以在当前的请求完成(阻止)之前停止接收新请求,但它违反了异步方法的目的。

理想情况下,人们会跟踪请求并在新请求出现时取消请求,并取代之前的请求。

下面是一个简化的别名$.ajax方法示例,以便为现有请求添加跟踪。

/*
 * Declare a function that will be used to load content and track previous requests
 *
 * Notice we're not assigning it to the function that is being defined on the same line,
 * ather that function is executing immeidately and its return value, a function, will
 * be assigned.
 */
var loadContent = (function () {
    // Privatized variable which will hold the last made request
    var last_request;

    /*
     * The actual definition of loadContent (notice it is being returned)
     * Takes a URL and options (just like $.ajax). it looks to see if there was a previous request and whether it has finished.
     * If there is a prev request, and it hasn't finished, it is aborted.
     *
     * @param string url Where to make the request
     * @param object opts optional $.ajax options
     */
    return function (url, opts) {
        // Look for previous request. If there is one, and it isn't finished, abort it
        if (last_request && last_request.ready_state !== 4) {
            last_request.abort();
        }

        // Make the new request, storing it into our tracked last request
        last_request = $.ajax(url, opts || {});

        // Return the (new) last request for use outside use of promises, etc if desired
        return last_request;
    };
}());

通过此功能发出的任何请求都将取消先前通过它发出的任何请求。请注意,您仍然可以将$.ajax或其他别名与此并行使用。您将通过此路由所有AJAX调用,这应该导致先前进行的类似中止调用,同时在其他代码区域中对$.ajax进行其他调用,这些调用与需要取代的特定功能无关。

以下是我最后一部分的含义示例:

鉴于此HTML:

<div>
    <ul>
        <li><a href="/aboutus.html" class="content-link">About</a></li>
        <li><a href="/services.html" class="content-link">Services</a></li>
    </ul>

    <div id="content">
        Main Site Content
    </div>

    <div id="feedContent">
        Perhaps something loading dynamically from a feed
    </div>
</div>

这个JS会让内容链接通过取消先前请求的代码,而不相关的内容会加载其他内容并且不会干扰(或受到干扰):

$(function () {
    var content_area = $('#content'),
        feed_area = $('#feedContent'),
        // Our previously defined function, minus all the comments
        loadContent = (function () {
            var last_request;

            return function (url, opts) {
                if (last_request && last_request.ready_state !== 4) {
                    last_request.abort();
                }

                last_request = $.ajax(url, opts || {});

                return last_request;
            };
        }());

    // Route main content links through the loadContent method which aborts previous, unfinished requests
    $('a.content-link').on('click', function (e) {
        e.preventDefault();

        loadContent($(this).attr('href'), {
            success: function (content) {
                content_area.html(content);
            }
        });
    });

    // Directly use $.ajax to load something unrelated
    $.ajax('/feed-url', {
        success: function (content) {
            feed_area.html(content);
        }
    });
});

答案 2 :(得分:0)

你问:

  

当其中一个jQuery帖子完成后会发生什么?由于javascripts单线程,while循环的执行是否会停止,脚本是否开始执行该帖子的回调?

但是在示例代码中,您声明:

  

我知道这是一个无限循环,但为了我的利益,我们只是忍受这个   例如,让我们按下浏览器将正确运行,而不会滞后,崩溃

该陈述使得无法回答这个问题。

如果我们被允许不做出这个假设并被允许准确解释真实浏览器中发生的事情,那么我们就可以回答这个问题。

所以这里是:

当其中一个jQuery帖子结束时会发生什么?

它永远不会完成。

那是因为浏览器永远不会发送网络数据包。

那是因为while循环正在运行。

为什么?这与事件循环的工作方式有关。这是一个简单的事件循环图:

Event loop
    ┌──────────┐
    │          │
    │          │
    │          ▼
    │        (1) check if there's any new ───────▶ parse data
    │        data on the network                        │
    │          │                                        │
    │          ▼                                        │
    │        (2) check if we need to execute  ◀─────────┘
    │        any javascript ─────────────────────▶ execute
    │          │                                  javascript
    │          ▼                                        │
    │        (3) check if we need to ◀──────────────────┘
    │        redraw the page  ──────────────────▶ redraw page
    │          │                                        │
    │          │                                        │
    └────◀─────┴─────────────────◀──────────────────────┘

您的while循环导致浏览器卡在步骤(2)中 - 执行javascript。

jQuery ajax函数不会向您的服务器发送ajax请求。相反,它只是在浏览器的网络代码中排队ajax请求。

浏览器在执行javascript时无法执行任何其他操作。它无法绘制到屏幕上,也无法发送网络数据包。那是因为它是单线程的。

一旦没有更多的javascript运行(一旦你的程序结束),浏览器最终会重新进入事件循环,并可以执行图形和网络代码。

当浏览器循环回到步骤(1)时,它最终将两个ajax请求发送到服务器。然后很长一段时间都没有发生(至少从CPU的角度来看)。在此期间,浏览器根本不使用您的CPU。如果查看流程管理器,您会发现CPU使用率几乎为零。

在此无操作时间内,由于浏览器不忙,因此可以处理用户事件,例如鼠标移动和鼠标点击。事件循环不会一直循环。当真的无事可做时,事件循环在步骤(1)中等待任何I / O暂停。

当服务器的数据包最终到达浏览器时,它会触发I / O事件,并检查是否有与该事件关联的回调。在这种情况下,它将检测到网络响应已与jQuery.ajax的回调相关联。因此,它从挂起的回调列表中提取回调,并再次输入步骤(2)来执行javascript。

当您的回调函数返回且没有其他需要运行的回调时,浏览器将进入步骤(3),然后返回步骤(1)并等待第二个服务器响应。

所有这些都一直在运行,直到没有更多待处理的回调。

现在,回答你的第二个问题:

  

我的问题的原因是:如果用户正在浏览我的网站,点击链接,我的javascript将请求发送到服务器,但是在请求响应到来之前,用户点击了一秒钟链接?这两个答案是否可能混合在一起?或者两个回复都将被执行,但是按照他们到达的顺序?

这两个回复可以按任何顺序完成 - 不一定是到达顺序。但这两个回应永远不会混在一起。那是因为回调与请求相关联。

有一个边缘案例。该关联是使用套接字进行的。这两个请求在不同的套接字上发送,浏览器会记住哪个套接字具有哪个回调。但HTTP允许服务器为每个套接字处理多个HTTP事务。这称为流水线。如果您的服务器实现HTTP流水线操作,则永远不允许在单个套接字内无序地发送响应(如果它们是两个独立的套接字,它仍然可以)。但是,如果您的服务器实现了错误的HTTP流水线代码,那么这两个请求可能会混淆。

因此,浏览器默认禁用流水线操作。但是一些高级用户(比如我)过去常常打开它。因此,在一个边缘情况下,响应可能会混淆(导致我试图调试它的世界受到伤害)。