【问题标题】:Design Pattern to avoid late returning promises overwriting faster ones?避免延迟返回的承诺覆盖更快的承诺的设计模式?
【发布时间】:2018-05-02 00:21:27
【问题描述】:

我正在深入了解如何避免在使用 Promise 加载图像时出现不必要的行为。我的实际使用非常复杂(一个大型的 Threejs 应用程序),因此我将下面的问题归结为一个更易于解释的示例。

我有一个显示图像的画廊 div。有一组缩略图,用户可以单击它加载与该缩略图关联的图像,一旦加载,它就会在画廊 div 中显示(在实际应用程序中,这里使用了一个承诺,因为我们必须加载图像,然后一旦加载创建一个在将纹理传回以应用于平面之前,从中获取纹理)。

我遇到的问题是,如果用户快速单击多个缩略图,它们会按照加载顺序应用到画廊 div,这可能意味着用户可以单击要加载的大图像,然后单击小图像会导致显示的小图像,但随后被较大的图像覆盖(即使它不是最后一个选择,因为它更大,因此加载时间更长)。

我不知道如何优雅地解决这个问题,希望有人能够就其他软件/程序如何处理这个问题提供建议。是否像拥有冗余系统一样简单地查看加载后是否仍然选择图像,如果没有则中止?

【问题讨论】:

  • 简单地防止加载图像的函数在它仍在进行中时被调用。这可以通过在请求开始时设置一个标志来完成,然后在请求完成后更新该标志。
  • ...这很有意义。
  • 试图通过更新上下文标志来解决这项工作是草率的工作。假设您的承诺没有得到解决或无限期地被拒绝。在此期间(如果它结束的话),您的标志将停留在false 并且您不能再提出任何请求。不..!这不是你想要的。看看this answer of mine关于取消先前承诺的类似主题。
  • 最简单的方法是存储一个滚动取消令牌,从而允许在创建新的之前取消之前的承诺。

标签: javascript asynchronous promise


【解决方案1】:

有许多不同的算法可用于处理此问题,部分选择过程取决于您想要的行为。根据您的描述,听起来您希望图像在加载后立即显示,但绝不会覆盖其后已显示的图像。这意味着如果图像按 1、2、3 的顺序请求,但按 2、1、3 的顺序到达,那么您将显示图像 2,然后显示图像 3,并且永远不会显示 1。

假设这是所需的算法,那么一种相当简单的做事方式就是为每个请求分配一个序列号,并跟踪您显示的最后一个序列号。每当任何图像完成加载时,您都会检查它的序列号是否高于显示的最后一个序列。如果不是,则不要显示它,因为它是旧图像被新图像超越。如果是这样,则显示它并将lastShownSequence 编号更新为该图像的序列号。此类算法需要两个持久变量来跟踪要分配的 lastShownSequence 编号和 nextSequenceNumber。

这是一个示例代码实现:

var lastShownImage = 0;
var nextSequenceNumber = 1;

// url is image url
// elem is image element in the page to replace with newly loaded image element
function loadImage(url, elem) {
    let imageSequenceNumber = nextSequenceNumber++;
    let img = new Image();
    img.onload = function() {
        // image loaded now
        // see if we should display it or not
        if (imageSequenceNumber > lastShownImage) {
            // hide current image so there's never any flashing of two images
            elem.style.display = "none";
            // record the sequence number we are showing
            lastShownImage = imageSequenceNumber;
            // insert new image
            let parent = elem.parentNode;
            parent.insertBefore(img, elem);
            // remove previous image
            parent.removeChild(elem);
        }
    };
    img.src = url;
}

如果您不希望 lastShownImagesequenceNumber 像这样开放范围,那么可以将它们封装在 IIFE 或您创建的类的实例中,但是因为我没有看到你将如何从你的代码中使用它,或者看看你是否需要一个更通用的多实例实现,我没有为此添加任何额外的复杂性。

【讨论】:

    猜你喜欢
    • 2012-07-08
    • 1970-01-01
    • 2017-11-24
    • 2017-04-26
    • 2016-12-08
    • 2016-09-22
    • 1970-01-01
    • 2015-08-25
    • 2015-03-27
    相关资源
    最近更新 更多