用于管理多个异步JavaScript操作的设计模式

jfr*_*d00 23 javascript design-patterns asynchronous callback

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

示例问题

我有一个启动序列,涉及许多异步进程(加载图像,等待定时器,进行一些ajax调用,进行一些初始化).一些异步流程可以同时启动(加载图像,AJAX调用),有些必须排序(运行AJAX调用#1,然后运行AJAX调用#2).现在,我已经完成了所有关闭回调函数和一系列全局状态的操作,这些状态跟踪已完成或未完成的内容.它有效,但它很乱,我有一些错误,因为确保你正确处理所有排序可能性和错误条件的复杂性.

当你遇到问题时,调试也很麻烦,因为它就像海森堡的不确定性原理.只要在序列中的任何位置设置断点,一切都会发生变化.您必须在代码中放置各种调试语句,以试图辨别正在发生的事情.

这是一个更具体的描述:

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

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

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

  4. 加载这两个图像时,还有一些显示内容要做.

  5. 完成所有操作后,您可以检查其中一个图像的显示时间,如果已经过了足够的时间,则显示下一个图像.如果没有,请在显示下一张图像之前再等一会儿.

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

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

编辑:我遇到了YUI的AsyncQueue,它不是我遇到的问题类型的完整解决方案,但是在同一个空间.对于排序或排序一堆异步操作似乎更多,但我不知道它对我所做的决策类型有何帮助.

Anu*_*rag 11

看看Promises/A的概念.jQuery用jQuery.Deferred对象实现了这个.

这是一篇很好的文章,展示了它对你的情况有用.我不久前问了一个类似的问题.


jfr*_*d00 4

现在,Promise 已成为 ES6 中的标准,并且许多优秀的 Promise 库都扩展了它以实现新功能和向后兼容性,看来 Promise 是解决这个问题的方法。

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

对于加载图像:

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

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

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

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

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

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

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

这种用法.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);
    });
});
Run Code Online (Sandbox Code Playgroud)