我有以下情况。我想从服务器加载文件A,然后服务器将尝试加载文件A1,A2,A3,... An和每个文件A [1-n]将依次加载其他文件,这可以继续;但它已经结束了。 我想使用延迟对象进行设置(以便不使用async:false挂起浏览器)但是加载和解析文件的递归使我对如何设置对象感到困惑。此外,要求在我继续使用级别(l-1)之前必须完成最高递归深度级别(l)。 对于没有递归的情况,这段代码可以工作,但递归的情况不包括在内。
var loadFile = function (index, url, scope, callback) {
$.ajax ({url: url, type: 'GET', dataType: 'text'})
.done (function (responseText) {
// store response in array
scope.requests[index] = responseText;
})
.fail (function (xhr, status, error) {
scope.requests[index] = 'error';
})
.always (function (responseText) {
// loop through entire response array from the beginning
for (var j = 0; j < scope.requests.length; j++)
{
if (scope.requests[j] === 'unprocessed') return;
else if (scope.requests[j] === 'error')
scope.requests[j] = 'done';
else if (scope.requests[j] !== 'done')
{
parseFile (scope.requests[j], scope, callback);
scope.requests[j] = 'done';
}
}
// if all are done then reset the array and run the callback
delete scope.requests;
if (callback) callback();
});
}
var parseFile = function (responseText, scope, callback) {
var lines = responseText.split("\n");
for (var l = 0; l < lines.length; l++) {
var line = lines[l];
line = line.replace (/^\s+/, ''); // remove leading white space
if (line.charAt(0) === '1') // file reference
{
var attrs = line.split (/\s+/);
// the file will exist in any of the paths in pathList
for (var i = 0; i < scope.pathList.length; i++) {
scope.requests.push ('unprocessed');
loadFile (++index, scope.pathList[i] + attrs[14], scope, callback);
}
}
}
}
var index = 0;
var this.requests = [];
this.requests.push ('unprocessed');
loadFile (index, fileAi, this, callback);
答案 0 :(得分:2)
基本理念是:
ajax
请求。由于您请求的某些文件不存在(这是预期的),您需要创建自己的Deferreds。然后,您可以从ajax done
和fail
回调中解决它们,从而有效地忽略失败。
另外,我根据您的要求添加了一个缓存对象。该对象将URL映射到promises。当您将done
回调附加到已经解析的承诺时,将使用相同的response
参数立即调用回调。这是一种很好的缓存方式,因为第一个请求不必完成,因为它是缓存请求而不是响应。因此,如果您在第一个请求完成之前请求相同的文件4次,它仍然只会导致一次ajax
次呼叫。
注意:由于我添加了函数getFile
,因此我们的注释中的范围/闭包问题不再是问题(因为每个dfd
变量现在都在函数范围内),所以代码是我想,有点不那么混乱了。问题是非常常见的循环scope issue。
代码:
// load all files, starting with startUrl.
// call callback when done.
var loadAll = function(startUrl, callback) {
var pathList = []; // assuming this has some base urls in it.
var dfds = []; // dfds for the current level.
var urls = [startUrl]; // urls for current level.
var responses = []; // responses for current level.
var cache = {}; // object to map urls to promises.
// given the responseText, add any referenced urls to the urls array
var parseFile = function (responseText) {
var lines = responseText.split("\n");
for (var l = 0; l < lines.length; l++) {
var line = lines[l];
line = line.replace (/^\s+/, ''); // remove leading white space
if (line.charAt(0) === '1') // file reference
{
var attrs = line.split (/\s+/);
// the file will exist in any of the paths in pathList
for (var i = 0; i < pathList.length; i++) {
// add new path to urls array
urls.push (pathList[i] + attrs[14]);
}
}
}
};
// load one file.
// check cache for existing promise for the url.
var getFile = function(url) {
var dfd;
if(cache.hasOwnProperty(url)){
// use cached promise.
// if it is already resolved, any callback attached will be called immediately.
dfd = cache[url];
dfds.push(cache[url]);
} else {
dfd = $.Deferred();
$.ajax ({url: url, type: 'GET', dataType: 'text'}).done(function(response){
// resolve and pass response.
dfd.resolve(response);
}).fail(function(){
// resolve and pass null, so this error is ignored.
dfd.resolve(null);
});
dfds.push(dfd.promise());
cache[url] = dfd.promise();
}
// when the request is done, add response to array.
dfd.done(function(response) {
if(response){
// add to responses array.
// might want to check if the same response is already in the array.
responses.push(response);
}
});
};
// request each file in the urls array.
// recurse when all requests done, or call callback.
var loadLevel = function () {
dfds = [];
responses = [];
for (var l = 0; l < urls.length; l++) {
getFile(urls[l]);
}
$.when.apply($, dfds).done(function(){
// entire level is done loading now.
// each done function above has been called already,
// so responses array is full.
urls = [];
// parse all the responses for this level.
// this will refill urls array.
for (var i = 0; i < responses.length; i++) {
parseFile(responses[i]);
}
if(urls.length === 0) {
// done
callback();
} else {
// load next level
loadLevel();
}
});
};
// load first level
loadLevel();
};
答案 1 :(得分:0)
我认为你不能用代码结构化的“逐级”块来实现,因为编写代码总是会在递归展开之前尝试完成整个分支,即给定这个结构:
1
/ \
2 6
/|\ |
3 4 5 7
它会跟随显示的数字顺序中的节点,而不是[1] [2 6] [3 4 5 7]
(或者你的意思是[3 4 5 7] [2 6] [1]
?)
我无法提供完整的解决方案,只是提示我认为会有所帮助。
您需要为每个级别创建一个数组,包含该级别每个请求文件的延迟对象。
您无法使用jqXHR
对象,因为您还会在.fail
案例中进行递归,因此您必须自己创建一个单独的$.Deferred()
然后.resolve
处理程序中的.always
。
使用$.when.apply($, myArray).done(...)
触发只有在myArray
中所有元素完成后才会发生的回调。