首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >Javascript承诺使用FileReader()

Javascript承诺使用FileReader()
EN

Stack Overflow用户
提问于 2015-12-28 22:58:27
回答 7查看 52.5K关注 0票数 35

我有以下HTML代码:

代码语言:javascript
复制
<input type='file' multiple>

下面是我的JS代码:

代码语言:javascript
复制
var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = function(){
    var fr = new FileReader();
    for(var i = 0; i < inputFiles.files.length; i++){
        fr.onload = function(){
            console.log(i) // Prints "0, 3, 2, 1" in case of 4 chosen files
        }
    }
    fr.readAsDataURL(inputFiles.files[i]);
}

所以我的问题是,我如何才能使这个循环同步?,,意思是,首先等待文件加载完成,然后再进入下一个文件。有人告诉我使用JS Promises。但是我不能去上班。这是我正在尝试的:

代码语言:javascript
复制
var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = function(){
    for(var i = 0; i < inputFiles.files.length; i++){
        var fr = new FileReader();
        var test = new Promise(function(resolve, reject){
            console.log(i) // Prints 0, 1, 2, 3 just as expected
            resolve(fr.readAsDataURL(inputFiles.files[i]));
        });
        test.then(function(){
            fr.onload = function(){
                console.log(i); // Prints only 3
            }
        });
    };
}

先谢谢你...

EN

回答 7

Stack Overflow用户

发布于 2017-10-04 23:03:02

我们对midos answer进行了修改,使其工作起来如下:

代码语言:javascript
复制
function readFile(file){
  return new Promise((resolve, reject) => {
    var fr = new FileReader();  
    fr.onload = () => {
      resolve(fr.result )
    };
    fr.onerror = reject;
    fr.readAsText(file.blob);
  });
}
票数 50
EN

Stack Overflow用户

发布于 2015-12-29 09:36:14

如果你想使用Promises按顺序(而不是同步)来做这件事,你可以这样做:

代码语言:javascript
复制
var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = function(){
  var promise = Promise.resolve();
  inputFiles.files.map( file => promise.then(()=> pFileReader(file)));
  promise.then(() => console.log('all done...'));
}

function pFileReader(file){
  return new Promise((resolve, reject) => {
    var fr = new FileReader();  
    fr.onload = resolve;  // CHANGE to whatever function you want which would eventually call resolve
    fr.onerror = reject;
    fr.readAsDataURL(file);
  });
}
票数 35
EN

Stack Overflow用户

发布于 2015-12-28 23:06:13

FileReader的本质是您不能使其操作同步。

我怀疑你并不真的需要或希望它是同步的,只是你想要正确地获得结果URL。建议使用promises的人可能是正确的,但这并不是因为promises使流程同步(它们不是),而是因为它们为处理异步操作(无论是并行还是串行)提供了标准化的语义:

使用promise,您可以从readAsDataURL的promise包装器开始(我在这里使用ES2015+,但您可以使用promise库将其转换为ES5 ):

代码语言:javascript
复制
function readAsDataURL(file) {
    return new Promise((resolve, reject) => {
        const fr = new FileReader();
        fr.onerror = reject;
        fr.onload = () => {
            resolve(fr.result);
        }
        fr.readAsDataURL(file);
    });
}

然后,您将使用我描述的基于promise的操作in this answer来并行读取这些操作:

代码语言:javascript
复制
Promise.all(Array.prototype.map.call(inputFiles.files, readAsDataURL))
.then(urls => {
    // ...use `urls` (an array) here...
})
.catch(error => {
    // ...handle/report error...
});

...or系列:

代码语言:javascript
复制
let p = Promise.resolve();
for (const file of inputFiles.files) {
    p = p.then(() => readAsDataURL(file).then(url => {
        // ...use `url` here...
    }));
}
p.catch(error => {
    // ...handle/report error...
});

在ES2017 async函数中,您可以使用await。对于并行版本,它没有做太多的事情:

代码语言:javascript
复制
// Inside an `async` function
try {
    const urls = await Promise.all(Array.prototype.map.call(inputFiles.files, readAsDataURL));
} catch (error) {
    // ...handle/report error...
}

...but它使该系列版本更简单、更清晰:

代码语言:javascript
复制
// Inside an `async` function
try {
    for (const file of inputFiles.files) {
        const url = await readAsDataURL(file);
        // ...use `url` here...
    }
} catch (error) {
    // ...handle/report error...
}

如果没有承诺,你可以通过跟踪你有多少未完成的操作来实现这一点,这样你就知道你什么时候完成了:

代码语言:javascript
复制
const inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = () => {
    const data = [];    // The results
    let pending = 0;    // How many outstanding operations we have

    // Schedule reading all the files (this finishes before the first onload
    // callback is allowed to be executed). Note that the use of `let` in the
    // `for` loop is important, `var` would not work correctly.
    for (let index = 0; index < inputFiles.files.length; ++index) {
        const file = inputFiles.files[index];
        // Read this file, remember it in `data` using the same index
        // as the file entry
        const fr = new FileReader();
        fr.onload = () => {
            data[index] = fr.result;
            --pending;
            if (pending == 0) {
                // All requests are complete, you're done
            }
        }
        fr.readAsDataURL(file);
        ++pending;
    });
};

或者,如果您出于某种原因希望按顺序读取文件(但仍然是异步的),则可以通过仅在前一次调用完成时安排下一次调用来实现:

代码语言:javascript
复制
// Note: This assumes there is at least one file, if that
// assumption isn't valid, you'll need to add an up-front check
var inputFiles = document.getElementsByTagName("input")[0];
inputFiles.onchange = () => {
    let index = 0;

    readNext();

    function readNext() {
        const file = inputFiles.files[index++];
        const fr = new FileReader();
        fr.onload = () => {
            // use fr.result here
            if (index < inputFiles.files.length) {
                // More to do, start loading the next one
                readNext();
            }
        }
        fr.readAsDataURL(file);
    }
};
票数 18
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34495796

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档