【问题标题】:Concept - Designing a collapsible queue for asynchronous resources概念 - 为异步资源设计可折叠队列
【发布时间】:2013-04-17 06:22:42
【问题描述】:

我注意到请求文件的大小会影响 ajax 调用的响应时间。因此,如果我对不同大小的文件发出 3 个 ajax GET 请求,它们可能会以任何顺序到达。我想要做的是在我将文件附加到 DOM 时保证顺序。

如何设置队列系统,以便在触发 A1->A2->A3 时。我可以保证它们按 A1->A2->A3 的顺序附加。

例如,假设 A2 先于 A1 到达。我希望该操作等到 A1 到达并加载。

一个想法是使用定时回调来创建状态检查器

// pseudo-code
function check(ready, fund) {
    // check ready some how
    if (ready) {
        func();
    } else {
        setTimeout(function () {
            check(ready, fund);
        }, 1); // check every msec
    }
}

但这似乎是一种资源繁重的方式,因为我每 1 毫秒触发一次相同的函数,直到资源被加载。

这是完成这个问题的正确路径吗?

【问题讨论】:

标签: javascript


【解决方案1】:

使用 1 毫秒定时回调的状态检查器 - 但这似乎是一种资源繁重的方式;这是完成这个问题的正确路径吗?

没有。你应该看看Promises。这样,您就可以轻松地像这样制定它:

var a1 = getPromiseForAjaxResult(ressource1url);
var a2 = getPromiseForAjaxResult(ressource2url);
var a3 = getPromiseForAjaxResult(ressource3url);

a1.then(function(res) {
    append(res);
    return a2;
}).then(function(res) {
    append(res);
    return a3;
}).then(append);

例如,jQuery 的.ajax 函数实现了这一点。

【讨论】:

【解决方案2】:

你可以试试这样的:

var resourceData = {};
var resourcesLoaded = 0;

function loadResource(resource, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
        var state = this.readyState;
        var responseCode = request.status;

        if(state == this.DONE && responseCode == 200) {
            callback(resource, this.responseText);
        }
    };

    xhr.open("get", resource, true);
    xhr.send();
}

//Assuming that resources is an array of path names
function loadResources(resources) {
    for(var i = 0; i < resources.length; i++) {
        loadResource(resources[i], function(resource, responseText) {

            //Store the data of the resource in to the resourceData map,
            //using the resource name as the key. Then increment the
            //resource counter.
            resourceData[resource] = responseText;
            resourcesLoaded++;

            //If the number of resources that we have loaded is equal
            //to the total number of resources, it means that we have
            //all our resources.
            if(resourcesLoaded === resources.length) {
                //Manipulate the data in the order that you desire.
                //Everything you need is inside resourceData, keyed
                //by the resource url. 
                ...
                ...
            }                    
        });
    }
}

如果某些组件必须在其他组件(如某些 JS 文件)之前加载和执行,您可以像这样将 AJAX 请求排队:

function loadResource(resource, callback) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
        var state = this.readyState;
        var responseCode = request.status;

        if(state == this.DONE && responseCode == 200) {
            //Do whatever you need to do with this.responseText
            ...
            ...

            callback();
        }
    };

    xhr.open("get", resource, true);
    xhr.send();
}

function run() {
    var resources = [
        "path/to/some/resource.html",
        "path/to/some/other/resource.html",
        ...
        "http://example.org/path/to/remote/resource.html"
    ];

    //Function that sequentially loads the resources, so that the next resource 
    //will not be loaded until first one has finished loading. I accomplish
    //this by calling the function itself in the callback to the loadResource 
    //function. This function is not truly recursive since the callback 
    //invocation (even though it is the function itself) is an independent call 
    //and therefore will not be part of the original callstack.
    function load(i) {
        if (i < resources.length) {
            loadResource(resources[i], function () {
                load(++i);
            });
        }
    }
    load(0);
}

这样,在前一个文件完成加载之前,不会加载下一个文件。

如果您不能使用任何第三方库,您可以使用我的解决方案。但是,如果您使用Bergi suggested 并使用Promises,您的生活可能会轻松得多。

【讨论】:

  • 这不允许同时发送所有 3 个 AJAX 请求。没有理由让每个 AJAX 请求在前一个请求完成之前等待发送。这实在是太低效了。
  • @Ian 如果你注意到,我在第一句话中提到这将按 sequential 顺序加载文件。如果 OP 可以进行权衡,那么他可以使用此解决方案。否则,他不必这样做。
  • 如何“加载”它们并不重要。无论哪种方式,OP当然希望它们按顺序加载,但是发出 AJAX 请求的方法不必是按顺序加载的。运行他们的“成功”回调是顺序部分
  • @Ian 我编写的代码需要按顺序完成,因为某些资源/组件必须在 加载其他资源/组件之前运行。无论哪种方式,我都添加了一些代码,让 OP 一次性触发 AJAX 请求,并且不会等到一个请求完成后才触发。
【解决方案3】:

无需每毫秒调用check(),只需在xhr 的onreadystatechange 中运行即可。如果您提供更多代码,我可以进一步解释。

【讨论】:

    【解决方案4】:

    我会有一个要执行的函数队列,每个函数都会在执行之前检查先前的结果是否已完成。

    var remoteResults[] 
    
    function requestRemoteResouse(index, fetchFunction) {
      // the argument fetchFunction is a function that fetches the remote content
      // once the content is ready it call the passed in function with the result.
      fetchFunction(
        function(result) { 
          // add the remote result to the list of results
          remoteResults[index] = result
          // write as many results as ready.
          writeResultsWhenReady(index);
        });
    }
    
    function writeResults(index) {
      var i;
      // Execute all functions at least once
      for(i = 0; i < remoteResults.length; i++) {
        if(!remoteResults[i]) {
          return;
        }
        // Call the function that is the ith result
        // This will modify the dom.
        remoteResults[i]();
        // Blank the result to ensure we don't double execute
        // Store a function so we can do a simple boolean check.
        remoteResults[i] = function(){}; 
      }
    }
    
    requestRemoteResouse(0, [Function to fetch the first resouse]);
    requestRemoteResouse(1, [Function to fetch the second resouse]);
    requestRemoteResouse(2, [Function to fetch the thrid resouse]);
    

    请注意,为简单起见,当前为 O(n^2),如果您在具有 hasRendered 属性的 remoteResults 的每个索引处存储一个对象,它会变得更快但更复杂。然后,您只会向后扫描,直到找到尚未发生的结果或已呈现的结果。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-22
      • 1970-01-01
      • 2015-04-12
      • 2021-04-29
      • 2016-08-05
      • 1970-01-01
      • 1970-01-01
      • 2014-10-08
      相关资源
      最近更新 更多