在JavaScript中,您可以强制循环在当前迭代完成之前不处理下一次迭代吗?

问题描述:

我有一个数据集,其中包含一个HTML5画布的图层数组。我使用fabric.js来管理画布的所有元素。应该按顺序加载图层,形成不同的场景。我通过forEach循环遍历数据集。 (编辑 - 进一步研究后,我看到for和for循环都是同步的)。在JavaScript中,您可以强制循环在当前迭代完成之前不处理下一次迭代吗?

问题是,一旦代码到达加载图像的位置,异步行为就会启动并且进程跳转到下一个循环以开始处理下一个图层。正如你可以想象的那样,这会导致图层在画布上出现乱序并最终失真的场景。

我一直在研究这个问题,并研究了几个星期,没有多少运气。

有什么办法可以防止forEach循环(或for循环)在当前结束之前不启动下一次迭代?

这里是我的foreach循环代码:

$.ajax({ 
    url: url, 
    method: "GET" 
}).done(function (data) { 
    data.forEach(function (data) { 
     switch (data.layer_type) { 
      case "texture": 
       { 
        addTexture(data) 
        break; 
       } 
      case "color": 
       { 
        addColor(data) 
        break; 
       } 
      case "room": 
       { 
        addBaseImg(data); 
        break; 
       } 
      case "decor": 
       { 
        addBaseImg(data); 
        break; 
       } 
      case "floor": 
       { 
        addBaseImg(data); 
        break; 
       } 
      default: 
       { 
        addOtherObjects(data); 
       } 
     } 
    }); 
}); 

这里是一个加载图像的情况下的一个例子:

最初的函数触发click事件:

function addTexture(data) { 
    $('img[data-obj-id="' + data.object_id + '"]').trigger("click"); 
} 
导致此功能的

$(document).on("click", ".img-patt", function() { 

    var imgURL = $(this).data("src"); 

    var _this = this; 

    //this is where the process skips to the next iteration, before the image is loaded and added to the canvas 

    fabric.Image.fromURL(imgURL, function (img) { 

     //some code to handle the pattern removed from here just to shorten up the snippet 

     var rect = new fabric.Rect({ 
      name: "texture", 
      visible: true, 
      selectable: false, 
      data: { 
       type: $(_this).data("type"), 
       objid: $(_this).data("obj-id") 
      }, 
      top: 0, 
      left: 0, 
      width: fabCanvas.width, 
      height: fabCanvas.height, 
      fill: pattern 
     }); 

     fabCanvas.add(rect); 
     fabCanvas.renderAll(); 
    }); 

}); 
+1

你号无法进行循环等待。但你可以模仿一个循环延迟使用递归 – Rajesh

+0

我最近[回答](https://*.com/a/43535024/3783478)类似的[问题](https://*.com/questions/43534797/keep -for - 环 - 从执行的功能于的JavaScript/43535024#43535024)。你可以尝试嵌入它 – Rajesh

+0

看看这个https://*.com/questions/16149431/make-function-wait-until-element-exists请告诉它是否有帮助! 干杯! –

如果你想手动处理它而不是依赖某种Promise实现(例如蓝鸟),你可以使用这个逻辑。

整体看起来比较复杂,但除了应该有效的事实之外,当你看到并认识到这一点时,添加应用知识,你就可以做更小,更好的事情。

这个概念不是在画布上添加图像,而是创建一个已经加载的对象的有序数组,然后当您全部加载它们时,在画布上添加该数组对象。

你的onclick变为:

function addToCanvas(object) { 
    canvas.add(object); 
} 

$(document).on("click", ".img-patt", function() { 

    var imgURL = $(this).data("src"); 

    addSomething(imgUrl, null, null, addToCanvas) 

}); 

你添加的东西是:

function addSomething(imgUrl, index, objectStack, callback) { 

     //this is where the process skips to the next iteration, before the image is loaded and added to the canvas 

     fabric.Image.fromURL(imgURL, function (img) { 

      //some code to handle the pattern removed from here just to shorten up the snippet 

      var rect = new fabric.Rect({ 
       name: "texture", 
       visible: true, 
       selectable: false, 
       data: { 
        type: $(_this).data("type"), 
        objid: $(_this).data("obj-id") 
       }, 
       top: 0, 
       left: 0, 
       width: fabCanvas.width, 
       height: fabCanvas.height, 
       fill: pattern 
      }); 

      objectStack && objectStack[index] = rect; 
      callback(rect) 
      }); 
} 

你的主循环变为:(for循环和foreach都是sincronous)

$.ajax({ 
    url: url, 
    method: "GET" 
}).done(function (data) { 
    var objectStack = new Array(data.length); 
    var loaded = data.length; 
    var onFinish = function() { 
     loaded-- 
     if (loaded === 0) { 
     objectStack.forEach(function(object) { 
      canvas.add(object); 
     }); 
     } 
    } 
    data.forEach(function (data, index) { 
     var src = $('img[data-obj-id="' + data.object_id + '"]').data('src'); 
     switch (data.layer_type) { 
      case "texture": 
       { 
        addSomething(src, index, objectStack, onFinish) 
        break; 
       } 
      case "color": 
       { 
        ... 
       } 
      case "room": 
       { 
        ... 
       } 
      case "decor": 
       { 
        ... 
       } 
      case "floor": 
       { 
        ... 
       } 
      default: 
       { 
        ... 
       } 
     } 
    }); 
}); 
+0

我喜欢这个想法,现在就试试吧! –

+0

我实施了这种方法,它工作完美!非常感谢。我能够使用相同的方法处理我的应用程序的另一部分,这些部分有类似的问题(由于从URL加载图像而导致元素加载失败),并修复了该问题。 –

你可能想要使用一个技巧,你应该得到你想要的。

在开始之前设置一个强制等待的变量。

现在我告诉你,你的情况

var wait = false; 
$.ajax({ 
    url: url, 
    method: "GET" 
}).done(function (data) { 
    wait = true; 
    data.forEach(function (data) { 
     switch (data.layer_type) { 
      case "texture": 
       { 
        addTexture(data) 
        break; 
       } 
      ... 
      default: 
       { 
        addOtherObjects(data); 
       } 
     } 
    }); 
    wait=false; 
}); 

,并在click事件,你必须这样做

$(document).on("click", ".img-patt", function() { 
    if (wait){ 
    // do samething 
    }else{ 

    } 
}); 

另外,如果你把一个定时器和一个功能变得更可靠

function DoSamething(){ 
    if (wait){ 
     // do samething 
    }else{ 
     window.setInterval(DoSamething, 1000); 
    } 
} 

您的点击事件将如此

$(document).on("click", ".img-patt", function() { 
    window.setInterval(DoSamething, 1000); 
});