处理相互依赖和/或分层的异步调用

Con*_*nce 11 javascript asynchronous waterfall loose-coupling

例如,假设我想从某处获取文件列表,然后加载这些文件的内容,最后将它们显示给用户.在同步模型中,它将是这样的(伪代码):

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这为用户提供了不错的反馈,如果我认为有必要,我可以将代码段移动到函数中.生活是简单的.

现在,粉碎我的梦想:fetchFiles()loadFile()实际上是异步的.简单的方法是将它们转换为同步函数.但是,如果浏览器锁定等待呼叫完成,这并不好.

如何处理多个相互依赖和/或分层的异步调用,而无需深入研究无穷无尽的回调链,以经典的简化方式进行传播?是否有一种经过验证的范例可以在保持代码松散耦合的同时干净地处理这些问题?

Eev*_*vee 6

延期真的是去这里的方式.它们准确地捕获了你(以及许多异步代码)想要的东西:"走开并做这个潜在的昂贵的事情,在此期间不要打扰我,然后在你回来时这样做."

而且你不需要jQuery来使用它们.一个有进取心的人已将Deferred移植到下划线,并声称您甚至不需要使用下划线来使用它.

所以你的代码看起来像这样:

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');
});
Run Code Online (Sandbox Code Playgroud)

设置有点啰嗦,但是一旦你编写了处理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!
});
Run Code Online (Sandbox Code Playgroud)


wes*_*wes 2

听起来你需要jQuery Deferred。以下是一些未经测试的代码,可能会帮助您指明正确的方向:

$.when(fetchFiles(source)).then(function(file_list) { 
  if (!file_list) {
    display('failed to fetch list');
  } else {
    for (file in file_list) {
      $.when(loadFile(file)).then(function(data){
        if (!data) {
          display('failed to load: ' + file);
        } else {
          display(data);
        }
      });
    }
  }
});
Run Code Online (Sandbox Code Playgroud)

我还发现了另一篇不错的文章,其中给出了 Deferred 对象的一些用例