Концепция - проектирование складной очереди для асинхронных ресурсов

Я заметил, что размер запрашиваемого файла будет влиять на продолжительность ответа на вызовы 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 мс, пока ресурсы не будут загружены.

Это правильный путь для решения этой проблемы?

Ответ 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 и используйте Promises.

Ответ 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) для простоты, оно будет быстрее, но сложнее, если вы сохранили объект в каждом индексе remoteResults, у которого было свойство hasRendered. Затем вы будете сканировать только до тех пор, пока не найдете результат, который еще не произошел, или тот, который был отображен.