【问题标题】:How do I ensure server request is complete before page redirect?如何在页面重定向之前确保服务器请求完成?
【发布时间】:2011-07-26 21:48:04
【问题描述】:

编辑:澄清一下,我的主要问题归结为:如果您向某个服务器(在本例中为 Google 的)发出 AJAX 请求,但随后客户端在服务器之前离开页面完成请求,服务器是否有可能(或可能)也会中止请求,或者服务器会尝试完成请求(尽管没有人再响应)?

如果您愿意,请随意阅读其余部分以了解所有细节。


我在旨在立即重定向的页面上使用 Google Analytics。由于 Google javascript 文件 ga.js 是异步加载的,因此我需要确保跟踪由 javascript 动态添加的所有脚本标签,并且页面重定向仅在这些脚本完成后发生。我已经处理了那部分。

文件ga.js 似乎向带有参数的__utm.gif 文件发出请求,这是执行实际跟踪的内容。该请求显然不是来自我可以控制的文件,所以这是我的问题:

首先,请求是异步的(我怀疑是)?其次,如何确保在重定向页面之前该请求有时间完成(我不希望在“足够的时间”过去后简单地重定向)?如果原始页面在请求完成之前重定向,请求是否仍然完成(毕竟,我不需要从 Google 返回任何数据)?

编辑:这是我的代码:

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
     <title>Redirecting...</title>

     <script type="text/javascript">
        /* <![CDATA[ */

        // Holds a value for each script. When all the values contained in this array are true, all loading has completed.
        var scripts = [];

        function scriptDetectLoaded(script)
        {
            scripts.push({ loaded: false, element: script });
            var index = scripts.length - 1;

            // Will set this script as loaded
            var callback = function ()
            {
                //console.log("Script with index " + index + " finished loading");
                scripts[index].loaded = true;
            };

            // For most browsers
            script.onload = callback;
            // For IE
            script.onreadystatechange = function ()
            {
                if (this.readyState == "complete")
                    callback();
            };

            return index;
        }

        /* ]]> */
     </script>

     <!-- Google analytics code -->
     <script type="text/javascript">
        /* <![CDATA[ */
        var _gaq = _gaq || [];
        _gaq.push(['_setAccount', 'UA-MY_ID-1']);
        _gaq.push(['_trackPageview']);

        (function ()
        {
            //debugger;
            var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
            ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
            scriptDetectLoaded(ga); var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
        })();
        /* ]]> */
    </script>
</head>
<body>
    <noscript>
        <p>Please enable JavaScript and refresh this page. Our site won't work correctly without it.</p>
        <p><a href="http://www.google.com/search?q=how+to+enable+javascript" target="_blank">How do I enable JavaScript?</a></p>
    </noscript>

<!-- Google Code for New Account Conversion Page -->
<script type="text/javascript">
    /* <![CDATA[ */
    //debugger;
    var google_conversion_id = SOME_ID;
    var google_conversion_language = "en";
    var google_conversion_format = "2";
    var google_conversion_color = "ffffff";
    var google_conversion_label = "SOME_LABEL";
    var google_conversion_value = 0;

    (function () {
        var gad = document.createElement('script'); gad.type = 'text/javascript'; gad.async = true;
        gad.src = document.location.protocol + "//www.googleadservices.com/pagead/conversion.js";
        scriptDetectLoaded(gad); var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(gad, s);
    })();
    /* ]]> */
</script>
<noscript>
    <div style="display:inline;">
        <img height="1" width="1" style="border-style:none;" alt="" src="http://www.googleadservices.com/pagead/conversion/1056222251/?label=keCSCNO9qAIQq9jS9wM&amp;guid=ON&amp;script=0"/>
    </div>
</noscript>

    <!-- Redirect Page -->
    <script type="text/javascript">
        /* <![CDATA[ */
        //debugger;
        var url = '/console';
        var interval = 100 /*milliseconds*/;
        var timeout = 5000 /*milliseconds*/;
        var count = 0;

        // Set a repeating function that checks to ensure all the scripts have loaded.
        // Once all the scripts have loaded, redirect the page.
        var id = setInterval(function ()
        {
            count += interval;

            // Check for timeout
            if (count > timeout)
            {
                //console.log("Timed out.");
                redirect();
            }

            // Check to make sure all scripts have loaded
            for (var i = 0, len = scripts.length; i < len; i++)
            {
                if (!scripts[i].loaded)
                    return;
            }

            // If we make it here, redirect
            redirect();
        }, interval);

        function redirect()
        {
            location.replace(url);

            // Run this in case the page doesn't redirect properly.
            clearInterval(id);

            var a = document.createElement("a");
            a.href = url;
            a.innerHTML = "Please click here to proceed";

            var p = document.getElementById("message");
            p.innerHTML = "";
            p.appendChild(a);
        }
        /* ]]> */
    </script>

    <p id="message">Please wait as we redirect the page.</p>
</body>
</html>

【问题讨论】:

    标签: javascript google-analytics xmlhttprequest


    【解决方案1】:

    如果您在请求完成之前重定向,它将在客户端被取消。意思是,服务器可能已经收到了帖子并对其采取了行动,但您不会得到结果。如果这些项目是动态添加的,则您不能使用任何 onload 类型的函数。我想你添加的谷歌文件会有某种类型的回调,你可以使用它来重定向你。

    编辑:如果您想以一种您将确切知道何时完成加载的方式加载脚本,以便您可以重定向到另一个页面,我将使用 jquery 的 $.getScript 函数。它允许在文件加载后进行回调,然后您可以立即重定向api.jquery.com/jQuery.getScript

    【讨论】:

    • 对,会在客户端取消。不过,我不需要从请求中返回任何东西(无论如何,我会得到一个 1x1 透明 gif),所以这没关系。如果客户端保释,服务器是否有可能取消请求?如果没有,我没什么好担心的。
    • 如果您只需要“加载”文件,您可以使用 setinterval 来查找该特定 js 文件中存在的变量名并在它存在后重定向
    • 张贴你的&lt;script&gt;标签之间的内容
    • 我的大部分文件都在script 标签之间,所以我发布了整个内容。
    • 哦,我知道你在做什么。我会使用 jquery 的 $.getScript 函数。它允许在文件加载后进行回调,然后您可以立即重定向api.jquery.com/jQuery.getScript
    【解决方案2】:

    对于依赖于 __utm.gif 的跟踪,不需要完成请求即可使指标起作用。一旦浏览器发送请求(带有所有参数),GA 就有了它需要的东西。所以你只需要确保图像请求被触发。

    如果这是一个没有实际内容的插页式页面,您可以执行检查文档图像集合长度等操作;当 length > 0 时,你知道 GA 已经创建了 IMG 并设置了它的 src(从而触发了请求)。不确定该系列的细节,但我知道曾经存在过类似的东西;也许它仍然存在。在这种情况下,一个简单的 setInterval 来执行测试并启动重定向就足够了。

    编辑:由于 GA 的东西将脚本添加到文档中,您可以检查 DOM 结构以查看是否存在具有正确 src 值的脚本。这可能会让您更接近于链接来自正确类型事件的重定向。

    【讨论】:

    • 遗憾的是,GA 没有为我“请求”的 gif 文件创建实际的 image 元素。他们使用gif 的原因有点超出我的理解。 AFAIK,他们只是将它用作一种超轻量级的资源来使事情变得更快,因为您不需要他们的任何响应。
    • 是的,我知道 GA 实际上并没有将 IMG 添加到页面中。但是,除了 DOM 之外,浏览器可能会跟踪窗口知道的此类资源集。查看有关 PPK 称为浏览器对象模型的文档。有点像浏览器有一个框架集合。然后,您不会通过检查 DOM 中是否存在 IMG,而是通过执行类似“document.images.length > 0”之类的操作来做出决定。
    • 我没有想到图像可以显示在图像集合中而不是在 DOM 中。无论如何,也许 Google 脚本不会将图像存储在任何地方,因为它从未出现在 document.images 中:/
    【解决方案3】:

    对于 ga.js 的当前状态,您可以使用以下代码检查跟踪是否完成:

    (function (global) {
    
        var listeners = []
            , ImageReworked
            , ImageNative = Image
        ;
    
        function stringMatch(haystack, needle) {
            if (typeof needle.valueOf() === 'string' && haystack.indexOf(needle) !== -1) {
                return true;
            } else if (needle instanceof RegExp && haystack.match(needle)) {
                return true;
            }
        }
    
        global.addImageListener = function (url, handler) {
            if (!listeners.length) {
                Image = ImageReworked;
            }
            listeners.push([url, handler]);
        };
    
        global.removeImageListener = function (url, handler) {
            var newListeners = [];
            listeners.forEach(function (el) {
                if (url.constructor !== el[0].constructor //this will not work for object in different windows
                    || el[0].toString() !== url.toString()
                    || (handler && handler !== el[1])) {
                    newListeners.push(el);
                }
            });
            listeners = newListeners;
            if (!listeners.length) {
                Image = ImageNative;
            }
        };
    
        ImageReworked = function(w, h) {
            var i = new ImageNative(w, h)
            ;
    
            function handler() {
                listeners.forEach(function (el) {
                    var url = el[0]
                        , handler = el[1]
                    ;
    
                    if (stringMatch(i.src, url)) {
                        handler(i.src);
                    }
                });
            }
    
            if (i.addEventListener) {
                i.addEventListener('load', handler, false);
            } else if (i.attachEvent) {
                i.attachEvent('onload', handler);
            }
    
            return i;
        };
    })(window);
    

    使用示例:

    addImageListener(/__utm/, function (src) {
        var parameters = {};
    
        window.removeImageListener(new RegExp('__utm'));//see, regexp can be created by new RegExp!
    
        (foundParameters = src.match(/(\?|&)(.*?\=[^?&]*)/g)) && foundParameters.forEach(function (parameter) {
            var pair = parameter.replace(/^(\?|&)/, '').split('=');
            parameters[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
        });
        console.log(parameters);
    });
    
    _gaq.push(['_setAccount', gaid], ['_trackPageview']);
    

    但您应该始终牢记,如果 GA 更改其跟踪逻辑,此代码将被破坏。

    您还应该添加超时(如果加载事件永远不会发生怎么办)。

    并且不要使用 i.onload,因为 GA-script 会覆盖此属性。

    【讨论】:

      猜你喜欢
      • 2017-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-25
      • 2013-01-28
      相关资源
      最近更新 更多