Концепция - Проектирование складной очереди для асинхронных ресурсов.
Я заметил, что размер запрашиваемого файла будет влиять на время ответа на вызовы ajax. Поэтому, если я запускаю 3 запроса ajax GET для файлов различного размера, они могут поступать в любом порядке. Что я хочу сделать, так это гарантировать порядок при добавлении файлов в DOM.
Как я могу настроить систему очередей, чтобы при запуске A1->A2->A3. Я могу гарантировать, что они добавляются как A1-> A2-> A3 в указанном порядке.
Например, предположим, что A2 прибывает до A1. Я хотел бы, чтобы действие ожидало прибытия и загрузки А1.
Одна идея состоит в том, чтобы создать проверку состояния, используя синхронизированный обратный вызов как таковой
// pseudo-code
function check(ready, fund) {
// check ready some how
if (ready) {
func();
} else {
setTimeout(function () {
check(ready, fund);
}, 1); // check every msec
}
}
но это похоже на ресурсоемкий способ, так как я запускаю одну и ту же функцию каждые 1 мсек, пока ресурсы не будут загружены.
Это правильный путь для решения этой проблемы?
4 ответа
проверка состояния с использованием обратного вызова с периодом 1 мсек - но это похоже на ресурсоемкий способ; Это правильный путь для решения этой проблемы?
Вы должны взглянуть на Обещания. Таким образом, вы можете легко сформулировать это так:
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's .ajax
функция реализует это.
Вы можете попробовать что-то вроде этого:
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, и использовать обещания.
Там нет необходимости звонить check()
каждую миллисекунду, просто запустите его в xhr onreadystatechange
, Если вы предоставите немного больше своего кода, я могу объяснить подробнее.
У меня была бы очередь функций для выполнения, и каждая из них проверяет предыдущий результат перед выполнением.
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. Тогда вы только отсканируете назад, пока не найдете результат, который еще не произошел, или результат, который был отрендерен.