2

我正在使用 html5 filereader API 在本地获取照片,而无需用户上传它们。

在支持它的浏览器中,我允许多项选择,因此我循环选择的文件并对每个文件执行一个功能(TC.editor.renderSlide()),如下所示:

 handlePhotoUploads: function(event) {
    var self = this;
    TC.dialog.destroy();
    var files = event.target.files;
    var count = files.length + TC.editor.cache.slides.length;
    for (var i = 0, f; f = files[i]; i++) {
        if (!f.type.match('image.*')) {
            continue;
        }
        var reader = new FileReader();
        var index = TC.editor.getNewSlidesIndex();
        TC.editor.addNewSlide('photo', index);
        reader.onload = (function(theFile) {
            var slideindex = index;
            return function(e) {
                TC.editor.renderSlide(slideindex, e.target.result, 'internal', count);
            };
        })(f);
        reader.readAsDataURL(f);
    }
},

问题是 TC.editor.renderSlide() 函数可能需要大量时间来执行(执行画布和 DOM 操作),因此在处理 8 个左右的大图像文件时,浏览器会被锁定很长时间。在此期间,甚至“处理 gif”也停止动画。

有没有办法确保每个 reader.onload 事件仅在前一个事件完成后触发?这样,用户可以通过查看每次调用 TC.editor.renderSlide() 的结果来定期更新进度,而不是在处理所有内容时锁定浏览器的当前情况?

我尝试向 renderSlide() 添加一个简单的 setTimeout 无济于事?

任何帮助或想法将不胜感激。

4

2 回答 2

3

事件确实按顺序触发onload,处理程序未执行,而当前正在处理另一个事件(“JS 是单线程的”)。当您在处理程序中启动一些异步处理(超时、动画、文件加载)时,这当然不计算在内。

在这种情况下,您将需要手动处理顺序处理。使用完成时执行的等待进程队列很容易:

var waiting = [];
// …create many parallel loaders, and on each:
    ….onevent = execute;

function execute(e) {
    if (waiting.active)
        waiting.push(e);
    else {
        waiting.active = true
        // do something that takes its time, supplying a
          … function callback(ready) {
            waiting.active = false;
            if (waiting.length)
                execute(waiting.shift()); // go on with next
          }
    }
}

但是,在我看来,不是renderSlide导致问题,而是您同时实例化了这么多FileReaders。通过将你的 for 循环变成一个异步调用的“递归”函数,让他们一个接一个地开始加载文件:

var files = […]
(function handle(i) {
     if (i >= files.length) return;
     // create loader
     ….onevent = function(e) {
          // do processing, and then call
          handle(i+1);
          // or call that from another callback if you want to do async
          // processing, like "renderSlide"
     };
})(0);
于 2012-10-31T21:48:58.053 回答
2

事件确实按顺序触发。Javascript 是单线程的,因此当您的一个事件处理程序正在执行时,不会触发其他事件,并且您的浏览器*仍然没有响应,因为它的单线程被运行您的 Javascript 事件处理程序占用。在 a中执行你的全部是不够renderSlide()的,因为在整个执行setTimeout期间浏览器仍将被锁定。renderSlide()您需要将内部 renderSlide()的实际逻辑分解成单独的块,每个块都可以推迟使用setTimeout(chunk, 1),给浏览器一点喘息的空间来保持响应能力。

*实际上,锁定的可能不是整个浏览器——它可能只是一个选项卡或一组在单个线程中运行的相关选项卡。实际的浏览器架构不同。

于 2012-10-31T21:47:39.703 回答