【问题标题】:How to use async/await with FileReader in for-loop function in google-apps-script web app?如何在 google-apps-script Web 应用程序的 for-loop 函数中使用 async/await 和 FileReader?
【发布时间】:2019-09-04 14:49:18
【问题描述】:

我创建了一个 Google Apps Script Web 应用程序,用于将图像上传到我的 Google Drive。它有很多行,每一行都有一个用于上传文件的输入标签。我创建了一个唯一的提交按钮来一次上传所有选择的图像。但是,我希望每一行都按顺序上传每个图像,然后在按顺序成功上传后删除该行。问题是我找不到使用 async/await 函数通过 FileReader 将图像上传到 Drive 的正确方法,因为当我运行脚本时,它仍然作为异步函数工作。

async function uploadImage() {
    var row   = document.getElementsByClassName('row');
    var file  = document.getElementsByClassName('img-file');
    var name  = document.getElementsByClassName('img-name');

    for (let i=0; i<row.length; i++) {
      var image = file[i].files[0];
      if (image) {
        var reader = new FileReader();
        reader.readAsDataURL(image);
        reader.onloadend = async (event) => { 
            await new Promise((resolve, reject) => { 
                google.script.run.withSuccessHandler(r => resolve())
                                 .uploadImgToDrive(name[i].value, event.target.result) 
            }).then(() => row[i].innerHTML='');
        }        
      }
    }    
}

【问题讨论】:

  • 现在有什么问题?有错误吗?
  • @TheMaster 问题是每行在成功上传时都没有按顺序删除。谢谢。
  • 但是不同的图片有不同的尺寸。 uploadImgToDrive 对于 image5 可能会更早返回,而 image1 可能会稍后完成,即使 image1 在 image5 之前开始上传。同步或排序可能需要更多时间。并行操作更快。
  • @TheMaster 是对的...但考虑到它还取决于吞吐量、延迟、API 限制等。我建议删除所有行并显示一个全局加载栏或让它们消失使用酷炫的动画订购。
  • @TheMaster 谢谢你的建议。

标签: javascript google-apps-script async-await filereader


【解决方案1】:

如果我理解您的目标是什么,下面的代码应该可以工作。一次只能上传一张图片。让我知道它是否符合您的需求。

请注意,您的函数将始终是异步的,因为您在其中有两个异步任务(FileReader 和 API 调用)。您唯一可以决定的是要“同时”处理多少个操作。

最后,请记住,无论何时您使用 async 函数,它都会立即返回一个未解析的 Promise,该 Promise 将使用函数完成运行时返回的值进行解析。

async 函数中,await 用于在继续之前“等待”一个承诺解决(在本例中,您使用new Promise() 创建的承诺),所以它类似于使用@987654325 @ 直接在 promise 上(你不需要两者,这就是为什么我删除了 .then() 部分)。


function uploadImages() {

    var row   = document.getElementsByClassName('row');
    var file  = document.getElementsByClassName('img-file');
    var name  = document.getElementsByClassName('img-name');

    (function nextImg(i) {
        var image = file[i].files[0];
        if (image) {
            var reader = new FileReader();
            reader.readAsDataURL(image);
            reader.onloadend = async (event) => {
                await new Promise((resolve, reject) => {
                    google.script.run.withSuccessHandler(r => resolve())
                        .uploadImgToDrive(name[i].value, event.target.result);
                });
                row[i].innerHTML='';
                if (i < row.length - 1) {
                  nextImg(i + 1);
                }
            };
        }
    })(0);

}

优化版(未测试):

避免使用innerHTML(重要)并尝试重用FileReader() 实例(不确定它是否会起作用)。


function uploadImages() {

    let row   = document.getElementsByClassName('row');
    let file  = document.getElementsByClassName('img-file');
    let name  = document.getElementsByClassName('img-name');

    let reader = new FileReader();

    (function nextImg(i) {
        if (file[i].files[0]) {
            reader.onloadend = async function onloadend(e) {
                await new Promise((resolve) => {
                    google.script.run.withSuccessHandler(r => resolve(r)).uploadImgToDrive(name[i].value, e.target.result);
                });
                while (row[i].firstChild) {
                    row[i].removeChild(row[i].firstChild);
                }
                if (i < row.length - 1) {
                    nextImg(i + 1);
                }
            };
            reader.readAsDataURL(file[i].files[0]);
        }
    })(0);

}

【讨论】:

  • @TheMaster 你是对的!我忘记了循环条件。已修复,谢谢!
  • 是的,我的目标是一次只上传一张图片,但在页面上按顺序循环所有行。非常感谢您的帮助。
【解决方案2】:

另一种方法是将readerloadend 事件连接到一个新的promise 并将其链接起来:

async function uploadImage() {
    var row   = document.getElementsByClassName('row');
    var file  = document.getElementsByClassName('img-file');
    var name  = document.getElementsByClassName('img-name');

    for (let i=0; i<row.length; i++) {
      var image = file[i].files[0];
      if (image) {
        var reader = new FileReader();
        reader.readAsDataURL(image);
        let promiseOfAllDone = new Promise(res=>reader.addEventListener('loadend',res))
            .then(event=>new Promise((resolve, reject) => { 
                    google.script.run.withSuccessHandler(resolve)
                                 .uploadImgToDrive(name[i].value, event.target.result)
             }).then(() => row[i].innerHTML='')
            .catch(e=>console.error(e));
        await promiseOfAllDone;//wait for all promises to be fulfilled
       }
    }
}

【讨论】:

    猜你喜欢
    • 2020-11-05
    • 2014-02-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-05
    • 1970-01-01
    • 1970-01-01
    • 2015-10-09
    相关资源
    最近更新 更多