.then()在循环期间未多次执行内部函数

时间:2018-07-31 22:39:57

标签: javascript jquery canvas promise

我正在处理实现诺言的一段代码。它在循环内,并且迭代输入到页面中的图像数量。首先,它应使用新图像更新画布,然后应运行一些代码以从画布图像获取信息。这是我当前的功能。

这就是将图像更改为输入中下一张图像的原因。

function readNextImage(i) {
  d = new $.Deferred();
  if (fileUpload.files && fileUpload.files[i]) {
    var FR = new FileReader();
    FR.onload = function(e) {
      fabric.Image.fromURL(e.target.result, function(img) {
        img.set({
          left: 0,
          top: 0,
          evented: false
        });
        img.scaleToWidth(canvas.width);
        img.setCoords();
        canvas.add(img);
      })
      var imgData = context.getImageData(0,0,canvas.width,canvas.height);
      d.resolve(imgData);
    };
    FR.readAsDataURL(fileUpload.files[i]);
  }
  return d.promise();
}

这是.then()命令中调用的另一个函数的代码段。

function read_image (imgData) {
    console.log("in read_image");
    data = imgData.data;
    /*doing some stuff to the data object*/ 
    console.log("here is some data output");
}

最后是调用这两个函数的地方

for (var image_num=0; image_num<fileUpload.files.length; image_num+=1){
    console.log("loop:"+image_num);
    promise = readNextImage(image_num).then(read_image);    
}

因此,正在发生的事情是使用新图像更新画布,但是只有在最终图像更新到画布之后,才是我从read_image()函数看到任何console.logs的时候,一个输出。这是控制台输出的示例。

loop:0
loop:1
loop:2
loop:3
loop:4
loop:5
loop:6
loop:8
in read_image
here is some data output

任何关于为何仅一次调用then()内部函数的见解都将受到赞赏。

1 个答案:

答案 0 :(得分:4)

您需要使用dletconstvar声明为函数的本地变量。您可能会覆盖范围较大的d,然后解决错误的问题。

function readNextImage(i) {
  var d = new $.Deferred();
//^^^

其他问题:

  1. FileReader对象没有错误处理,因此任何错误都将导致您的承诺永远不会解决或拒绝。
  2. 您无需等待fabric.Image.fromURL()(可能是异步的)完成,然后再兑现您的承诺。
  3. 如果您对if的声明不满意,if (fileUpload.files && fileUpload.files[i]),那么您将永远无法解决或拒绝承诺。

这是尝试清除这四个问题(无法测试代码以确保其完全正确):

function readNextImage(i) {
    var d = new $.Deferred();
    if (fileUpload.files && fileUpload.files[i]) {
        var FR = new FileReader();
        FR.onload = function(e) {
            fabric.Image.fromURL(e.target.result, function(img) {
                img.set({
                    left: 0,
                    top: 0,
                    evented: false
                });
                img.scaleToWidth(canvas.width);
                img.setCoords();
                canvas.add(img);
                var imgData = context.getImageData(0, 0, canvas.width, canvas.height);
                d.resolve(imgData);
            });
        };
        FR.onerror = function(e) {
            d.reject(e);
        }
        // start the file reader
        FR.readAsDataURL(fileUpload.files[i]);
    } else {
        // not sure what you want to do here, but you need to resolve or reject the promise
        d.resolve()
    }
    return d.promise();
}

当编写带有尚未处理promise的多个异步操作的代码时,通常最好在最低级别“保证”每个异步操作。然后,这使您可以使用序列和承诺的流控制以及错误处理来一致地编写所有程序逻辑(这是一个很大的优势)。在这种情况下,您可以这样做:

function readFileDataURL(f) {
    return new Promise(function(resolve, reject) {
        var fr = new FileReader();
        fr.onload = resolve;
        fr.onerror = reject;
        fr.readAsDataURL(f);
    });
}

function imageFromURL(data) {
    return new Promise(function(resolve, reject) {
        fabric.Image.fromURL(data, resolve)
    });
}

function readNextImage(i) {
    if (fileUpload.files && fileUpload.files[i]) {
        return readFileDataURL(fileUpload.files[i]).then(function(e) {
            return imageFromURL(e.target.result);
        }).then(function(img) {
            img.set({
                left: 0,
                top: 0,
                evented: false
            });
            img.scaleToWidth(canvas.width);
            img.setCoords();
            canvas.add(img);
            var imgData = context.getImageData(0,0,canvas.width,canvas.height);
            return imgData;
        });
    } else {
        // nothing to do here
        return Promise.resolve();
    }
}

注意,您还会得到两个实用程序函数,它们可以保证可以在其他地方重用。