我正在寻找一个好的设计模式来跟踪一堆不同的异步JavaScript活动(图像加载,多个AJAX调用,有序的AJAX调用等等),这比仅仅很多自定义回调和自定义状态更好变量。你建议我用什么?是否有任何类型的队列系统能够具有超越排序的逻辑?
我有一个启动序列,涉及许多异步进程(加载图像,等待定时器,进行一些ajax调用,进行一些初始化)。一些异步流程可以同时启动(加载图像,AJAX调用),有些必须排序(运行AJAX调用#1,然后运行AJAX调用#2)。现在,我已经完成了所有关闭回调函数和一系列全局状态的操作,这些状态跟踪已经完成或未完成的事情。它有效,但它很乱,我有一些错误,因为确保你正确处理所有排序可能性和错误条件的复杂性。
当你遇到问题时,调试也很麻烦,因为它就像海森堡的不确定性原则。只要在序列中的任何位置设置断点,一切都会发生变化。您必须在代码中放置各种调试语句,以试图辨别正在发生的事情。
以下是更具体的说明:
加载了三张图片。加载一个特定图像后,我想显示它。一旦显示一段时间后,我想显示第二张图像。第三个进入队列以便稍后显示。
有三个AJAX调用必须按连续顺序发生(一个的输出用作下一个输入的一部分)。
当完成AJAX调用时,会对结果进行大量的JS处理,然后需要再加载两个图像。
当加载这两个图像时,还有一些显示内容要做。
完成所有操作后,您可以检查其中一个图像的显示时间,如果已经过了足够的时间,则显示下一个图像。如果没有,请等待一段时间再显示下一张图像。
每个步骤都有成功和错误处理程序。一些错误处理程序启动了仍可成功继续的备用代码。
我不希望任何人在这里遵循确切的过程,只是为了让人们了解这些步骤之间的逻辑类型。
编辑:我遇到YUI's AsyncQueue,这不是我遇到的问题类型的完整解决方案,但是在同一个空间。对于排序或排序一堆异步操作似乎更多,但我不知道它对我所做的决策类型有何帮助。
答案 0 :(得分:11)
看看Promises/A的概念。 jQuery使用jQuery.Deferred
对象实现它。
答案 1 :(得分:4)
现在ES6中的promises标准以及许多优秀的承诺库扩展了新功能和向后兼容性,似乎承诺是这里的方式。
解决方案首先执行每个异步操作并创建一个返回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;
});
}
进行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()
的承诺版本,以便可以与其他承诺操作链接:
// 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()
的使用显示了一种经典的设计模式,用于使用promises对数组上的一系列操作进行排序。
有三个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 :(得分:0)
虽然 Promises 为我们提供了更清晰的代码,但我们可以通过 Generators 做得更好。我写了一篇关于如何在Tame Async JavaScript with ES6中使用生成器的帖子。使用所描述的模式将使处理复杂的异步交互更容易推理,并为ES7计划的await
能力建立心理模型。