【问题标题】:Design pattern for managing multiple asynchronous JavaScript operations用于管理多个异步 JavaScript 操作的设计模式
【发布时间】:2011-10-03 07:43:22
【问题描述】:

我正在寻找一种很好的设计模式来跟踪一堆不同的异步 JavaScript 活动(图像加载、多个 AJAX 调用、顺序 AJAX 调用等……),这比仅仅使用大量自定义回调和自定义状态要好变量。你会建议我用什么?是否有任何类型的队列系统具有超越排序的逻辑能力?

示例问题

我有一个启动序列,其中涉及许多异步进程(加载图像、等待计时器、进行一些 ajax 调用、进行一些初始化)。一些异步进程可以同时启动(加载图像、AJAX 调用),而一些必须按顺序运行(运行 AJAX 调用 #1,然后 AJAX 调用 #2)。现在,我已经让所有东西都运行在回调函数和一堆全局状态中,这些状态可以跟踪已完成或未完成的内容。它可以工作,但是很混乱,而且我遇到了一些错误,因为确保您处理所有排序可能性的正确和错误条件很复杂。

当你遇到问题时,调试也是相当痛苦的,因为它就像海森堡不确定性原理。只要您在序列中的任何位置设置断点,一切都会改变。你必须在代码中加入各种调试语句来尝试辨别发生了什么。

这里有一个更具体的描述:

  1. 加载了三个图像。一旦加载了一个特定的图像,我就想显示它。一旦它显示了一段时间,我想显示第二张图像。第三个进入队列以供稍后显示。

  2. 三个 AJAX 调用必须以连续顺序发生(其中一个的输出用作下一个输入的一部分)。

  3. AJAX 调用完成后,需要对结果进行一堆 JS 处理,然后需要加载另外两个图像。

  4. 加载这两个图像后,还有一些显示工作要做。

  5. 所有这些都完成后,您将检查其中一个图像显示了多长时间,如果已经过了足够的时间,则显示下一个图像。如果没有,请稍等片刻再显示下一张图片。

每个步骤都有成功和错误处理程序。一些错误处理程序启动了仍然可以成功继续的备用代码。

我不希望任何人在这里遵循确切的过程,只是为了让人们了解步骤之间的逻辑类型。

编辑:我遇到了YUI's AsyncQueue,这不是我遇到的问题类型的完整解决方案,但在同一个空间。它似乎更适合对一堆异步操作进行排序或排序,但我看不出它对我的决策类型有何帮助。

【问题讨论】:

  • 你有没有看Lab.js,我想应该能帮到你
  • 我对lab.js 的第一次阅读是它用于以受控顺序尽可能快地加载多个脚本。它是否适合对来自同一个脚本的各种类型的异步操作进行排序?
  • 希望看到对此问题的更多回复。肯定还有更多,因为它现在是一个普遍的问题。

标签: javascript design-patterns asynchronous callback


【解决方案1】:

随着 promises 现在在 ES6 中成为标准,并且许多优秀的 promise 库扩展了它以实现新功能和向后兼容性,看来 promises 是要走的路。

解决方案首先进行每个异步操作并创建一个返回承诺的包装器:

用于加载图像:

function loadImage(url) {
    return new Promise(function(resolve, reject) {
        var img = new Image();
        img.onload = function() {
            resolve(img);
        };
        img.onerror = img.onabort = function() {
            reject(url);
        };
        img.src = url;
    });
}

用于进行 Ajax 调用(简化版):

function ajaxGet(url) {
    return new Promise(function(resolve, reject) {
        var req = new XMLHttpRequest();
        req.addEventListener("load", resolve);
        req.addEventListener("error", reject);
        req.addEventListener("abort", reject);
        req.open("GET", url);
        req.send();
    });
}

为了在执行操作之前等待特定的时间,您甚至可以创建一个 setTimeout() 的 Promise 版本,以便它可以与其他 Promise 操作链接:

// delay, return a promise
// val is optional
function delay(t, val) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(val);
        }, t);
    });
}

现在,可以将这些组合起来以创建问题所要求的逻辑:

加载了三张图片。一旦加载了一个特定的图像,我就想显示它。一旦它显示了一段时间,我想显示第二张图像。第三个进入队列以供稍后显示。

// start all three images loading in parallel, get a promise for each one
var imagePromises = [url1, url2, url3].map(function(item) {
    return loadImage(item);
});

// display the three images in sequence with a delay between them
imagePromises.reduce(function(p, item) {
    return p.then(function() {
        // when the next image is ready display it
        return item.then(function(img) {
            displayImage(img);
            return delay(15 * 1000);
        });
    });
}, Promise.resolve());

.reduce() 的这种用法展示了一种经典的设计模式,它使用 Promise 对数组上的一系列操作进行排序。

三个 AJAX 调用必须以连续的顺序发生(其中一个的输出用作下一个输入的一部分)。

AJAX 调用完成后,需要对结果进行一堆 JS 处理,然后需要加载另外两个图像。

var p = ajaxGet(url1).then(function(results1) {
    // do something with results1
    return ajaxGet(url2);
}).then(function(results2) {
    // do something with results2
    return ajaxGet(url3); 
}).then(function(results3) {
    // process final results3 here
    // now load two images and when they are loaded do some display stuff
    return Promise.all(loadImage(imgx), loadImage(imgy)).then(function(imgs) {
        doSomeDisplayStuff(imgs);
    });
});

【讨论】:

    【解决方案2】:

    虽然 Promises 为我们提供了更简洁的代码,但我们可以使用 Generators 做得更好。我在Tame Async JavaScript with ES6 中写了一篇关于如何使用生成器的帖子。使用所描述的模式将使处理复杂的异步交互更容易推理,并为 ES7 计划的await 功能建立一个心智模型。

    【讨论】:

    • 答案的所有“肉”都在外部资源中而不是在实际答案中的答案在这里被认为是不合适的。如果您可以在您的实际答案中包含文章的内容,这将使它成为一个更好、更有意义的答案。仅供参考,我在阅读您的文章时遇到了麻烦。我非常了解承诺,但根本不了解生成器。如果您可以在答案中更详细地解释它,那就太好了。
    【解决方案3】:

    看看Promises/A的概念。 jQuery 使用 jQuery.Deferred 对象实现了这一点。

    这是一个很好的article,展示了它对您的情况有何帮助。不久前我问过类似的question

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-11-25
      • 1970-01-01
      • 2016-06-10
      • 2011-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多