在JavaScript中,你能强制一个循环在当前迭代完成之前不处理下一次迭代吗?
In JavaScript, can you force a loop to not process the next iteration before the current iteration is complete?
我有一个数据集,其中包含 HTML5 canvas 的图层数组。我正在使用 fabric.js 来管理我的 canvas 的所有元素。这些图层应该按顺序加载,形成不同的场景。我通过 forEach 循环遍历数据集。 (编辑——经过进一步研究,我发现 for 和 forEach 循环都是同步的)。
问题是,一旦代码到达加载图像的位置,异步行为就会开始,进程会跳转到循环的下一次迭代以开始处理下一层。可以想象,这会导致图层在 canvas 上出现乱序并最终扭曲场景。
我已经为此工作并研究了数周,但运气不佳。
有什么方法可以防止 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();
});
});
你可能想使用一个技巧,你应该得到你想要的。
在开始 for 之前设置一个强制等待的变量。
现在我给你看一下你的情况
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);
});
如果您想手动处理它而不是依赖某种 Promise 实现(例如 bluebird),您可以使用此逻辑。
整体看起来更复杂,但除了实际应该可行之外,当您看到并理解这一点时,添加您的应用知识,您将能够做一些更小、更好的事情。
这个概念不是在 canvas 上添加图像,而是创建一个已加载对象的有序数组,然后当您全部加载它们时,将该对象数组添加到 canvas。
你的点击变成:
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:
{
...
}
}
});
});
我有一个数据集,其中包含 HTML5 canvas 的图层数组。我正在使用 fabric.js 来管理我的 canvas 的所有元素。这些图层应该按顺序加载,形成不同的场景。我通过 forEach 循环遍历数据集。 (编辑——经过进一步研究,我发现 for 和 forEach 循环都是同步的)。
问题是,一旦代码到达加载图像的位置,异步行为就会开始,进程会跳转到循环的下一次迭代以开始处理下一层。可以想象,这会导致图层在 canvas 上出现乱序并最终扭曲场景。
我已经为此工作并研究了数周,但运气不佳。
有什么方法可以防止 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();
});
});
你可能想使用一个技巧,你应该得到你想要的。
在开始 for 之前设置一个强制等待的变量。
现在我给你看一下你的情况
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);
});
如果您想手动处理它而不是依赖某种 Promise 实现(例如 bluebird),您可以使用此逻辑。
整体看起来更复杂,但除了实际应该可行之外,当您看到并理解这一点时,添加您的应用知识,您将能够做一些更小、更好的事情。
这个概念不是在 canvas 上添加图像,而是创建一个已加载对象的有序数组,然后当您全部加载它们时,将该对象数组添加到 canvas。
你的点击变成:
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:
{
...
}
}
});
});