从使用事件对象的嵌套 FileReader onload 函数回调

Callback from nested FileReader onload function that uses event object

我几乎已经掌握了理论,但对细节却一头雾水。图像大小调整函数,给定一个 var $data(这是更改后文件输入的 event object)将 return 一个 var dataurl 返回(这是一个 dataurl jpeg).

我的问题是让 dataurl 识别它是在嵌套函数内更新的。正如我在下面显示的那样,代码正在将 data:image/jpg;base64 记录到控制台,显示 dataurl 未更新。

研究让我发现这是由于 异步 调用,这是有道理的。在继续并打印到控制台之前,var 没有及时更改。我还了解到这是发出 callback 更新 var.

的标准情况

尝试了几次后,我找不到使用 callback 更新 var 的干净(或有效)方法。在显示我当前的代码后,我将对此进行扩展:

function clProcessImages($data) {

        var filesToUpload = $data.target.files;
        var file = filesToUpload[0];
        var canvas = document.createElement('canvas');
        var dataurl = 'data:image/jpg;base64';

        var img = document.createElement("img");

        var reader = new FileReader();
        reader.onload = function(e, callback) {

            img.onload = function () {

                var ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0);

                var MAX_WIDTH = 800;
                var MAX_HEIGHT = 600;
                var width = img.width;
                var height = img.height;

                if (width > height) {
                    if (width > MAX_WIDTH) {
                        height *= MAX_WIDTH / width;
                        width = MAX_WIDTH;
                    }
                } else {
                    if (height > MAX_HEIGHT) {
                        width *= MAX_HEIGHT / height;
                        height = MAX_HEIGHT;
                    }
                }
                canvas.width = width;
                canvas.height = height;
                var ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0, width, height);
                dataurl = canvas.toDataURL("image/jpeg",0.8);
                callback(dataurl);

            }
            img.src = e.target.result

        }
        reader.readAsDataURL(file);

        console.log(dataurl); // console log mentioned in question
        return dataurl;

    };

这是目前的代码,正在弄清楚如何实现 callback。我知道在其显示状态下未调用回调。

我的第一个障碍是我(似乎)以后不能再次调用 reader.onload 函数,因为它似乎是一个专门在加载对象时触发的事件,而不是调用的东西随时手动。我最好的猜测是创建一个 reader.onload 会触发的新函数,它将包含所有当前 reader.onload 函数,我可以稍后直接指定,以发出 callback,跳过引用onload 事件。

似乎是一个合理的理论,但后来我被 onload 函数通过的 event object 卡住了。假设我应用上述理论并且 onload 事件调用新函数 readerOnLoad(e)。它会通过 event object 罚款。但是,当稍后想要执行回调时,我怎么能手动调用 readerOnLoad(e),因为我看不到以优雅的方式从同一个 FileReader 传递 event object 的方法。感觉有点像我误入了非常混乱的代码*来解决,而感觉应该有一些更优雅的东西来解决不太小众的情况。

*原始$data event object变量是在文件输入检测到变化时通过存储全局变量获得的。理论上,我可以创建一个全局变量来存储 FileReader onload 的 event object。感觉很复杂,因为我的原始代码和以这种方式运行的代码之间的区别非常严重且不优雅,而唯一的区别是更新未及时准备好的 var。

使用异步函数时,您通常希望将函数作为回调传递。您可以像这样使用 NodeJS 样式回调:

// asyncTask is an asynchronous function that takes 3 arguments, the last one being a callback

asyncTask(arg1, arg2, function(err, result) {
  if (err) return handleError(err);
  handleResult(result);
})

所以在你的情况下,你可以这样做:

clProcessImages($data, function(dataurl) {
  console.log(dataurl);
  // Do you other thing here
});

function clProcessImages($data, onProcessedCallback) {
  var filesToUpload = $data.target.files;
  var file = filesToUpload[0];
  var canvas = document.createElement('canvas');
  var dataurl = 'data:image/jpg;base64';

  var img = document.createElement("img");

  var reader = new FileReader();
  reader.onload = function(e) {

    img.onload = function () {

      var ctx = canvas.getContext("2d");
      ctx.drawImage(img, 0, 0);

      var MAX_WIDTH = 800;
      var MAX_HEIGHT = 600;
      var width = img.width;
      var height = img.height;

      if (width > height) {
        if (width > MAX_WIDTH) {
          height *= MAX_WIDTH / width;
          width = MAX_WIDTH;
        }
      } else {
        if (height > MAX_HEIGHT) {
          width *= MAX_HEIGHT / height;
          height = MAX_HEIGHT;
        }
      }
      canvas.width = width;
      canvas.height = height;
      var ctx = canvas.getContext("2d");
      ctx.drawImage(img, 0, 0, width, height);
      dataurl = canvas.toDataURL("image/jpeg",0.8);
      onProcessedCallback(dataurl);

    }
    img.src = e.target.result

  }
  reader.readAsDataURL(file);

  return dataurl;
};

您可能还想像第一个示例一样将 error 对象传递给回调,并适当地处理它。

简而言之,始终将回调函数作为参数添加到您自己的异步函数中。