举个例子,假设我想从某处获取文件列表,然后加载这些文件的内容,最后将它们显示给用户。在同步模型中,它将是这样的(伪代码):
var file_list = fetchFiles(source);
if (!file_list) {
display('failed to fetch list');
} else {
for (file in file_list) { // iteration, not enumeration
var data = loadFile(file);
if (!data) {
display('failed to load: ' + file);
} else {
display(data);
}
}
}
这为用户提供了不错的反馈,如果我认为有必要,我可以将代码片段移动到函数中。生活很简单。
现在,粉碎我的梦想:fetchFiles()
和loadFile()
实际上是异步的。简单的出路是将它们转换为同步函数。但如果浏览器锁定等待调用完成,这就不好了。
如何处理多个相互依赖和/或分层的异步调用,而无需以经典的reductio ad spaghettum 方式深入研究无穷无尽的回调链?是否有经过验证的范例可以干净地处理这些问题,同时保持代码松散耦合?
最佳答案
Deferreds 确实是实现这一目标的方式。它们准确地捕获了您(以及大量异步代码)想要的内容:“走开去做这件可能代价高昂的事情,在此期间不要打扰我,然后在您回来时再做这件事。”
而且您不需要 jQuery 来使用它们。一个有进取心的人有ported Deferred to underscore ,并声称您甚至不需要下划线即可使用它。
所以你的代码可以是这样的:
function fetchFiles(source) {
var dfd = _.Deferred();
// do some kind of thing that takes a long time
doExpensiveThingOne({
source: source,
complete: function(files) {
// this informs the Deferred that it succeeded, and passes
// `files` to all its success ("done") handlers
dfd.resolve(files);
// if you know how to capture an error condition, you can also
// indicate that with dfd.reject(...)
}
});
return dfd;
}
function loadFile(file) {
// same thing!
var dfd = _.Deferred();
doExpensiveThingTwo({
file: file,
complete: function(data) {
dfd.resolve(data);
}
});
return dfd;
}
// and now glue it together
_.when(fetchFiles(source))
.done(function(files) {
for (var file in files) {
_.when(loadFile(file))
.done(function(data) {
display(data);
})
.fail(function() {
display('failed to load: ' + file);
});
}
})
.fail(function() {
display('failed to fetch list');
});
设置有点冗长,但是一旦您编写了处理 Deferred 状态的代码并将其塞入某处的函数中,您就不必再担心它了,您可以尝试实际流程事件很容易。例如:
var file_dfds = [];
for (var file in files) {
file_dfds.push(loadFile(file));
}
_.when(file_dfds)
.done(function(datas) {
// this will only run if and when ALL the files have successfully
// loaded!
});
关于javascript - 处理相互依赖和/或分层的异步调用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14407744/