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

时间:2017-06-04 05:27:53

标签: javascript fabricjs

我有一个数据集,其中包含HTML5画布的图层数组。我正在使用fabric.js来管理我画布的所有元素。应该按顺序加载图层,形成不同的场景。我通过forEach循环遍历数据集。 (编辑 - 经过进一步研究,我发现for和forEach循环都是同步的)。

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

我一直在研究这个并且研究它已经好几个星期而且运气不好。

有没有办法阻止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);
                }
        }
    });
});

以下是加载图片的案例示例:

初始函数触发点击事件:

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();
    });

});

2 个答案:

答案 0 :(得分:0)

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

整体看起来更复杂,但除了应该工作的事实,当你看到并承担这一点,添加你的应用知识,你将能够做更小,更好的事情。

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

你的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都是同步的)

$.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:
                {
                    ...
                }
        }
    });
});

答案 1 :(得分:-2)

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

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

现在我告诉你你的情况

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;
});

在点击事件中你必须这样做

$(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);
});