如何确定已读取所有文件并解决承诺

以下代码负责读取文件。 我的要求是如何查找是否已读取所有文件,以便我可以从父函数(readmultifiles)返回或解析promise。

$.when(readmultifiles(files)) .then(function(){//all files uploaded})) 

上面的代码启动文件读取。 可以做什么,以便在读取所有文件时完成回调或可以返回。

  function readmultifiles(files) { // Read first file setup_reader(files, 0); } function setup_reader(files, i) { var file = files[i]; var name = file.name; var reader = new FileReader(); reader.onload = function(e) { readerLoaded(e, files, i, name); }; reader.readAsBinaryString(file); // After reading, read the next file. } function readerLoaded(e, files, i, name) { // get file content var bin = e.target.result; // do sth with text // If there's a file left to load if (i < files.length - 1) { // Load the next file setup_reader(files, i + 1); } } 

在使用您的实现可以学习的承诺的好设计中,有几件事需要考虑:

  1. 从您拥有的最低级异步操作创建一个承诺(称为“promisify”)。 然后,您可以使用promisefunction来控制逻辑流并传播错误,并且您的代码将始终使用promises实现。 在这种情况下,它意味着你应该宣传readFile() 。 它还使readFile()在项目或未来项目的其他地方更有用。
  2. 确保始终正确传播错误。 使用异步代码时,如果不使用promises,则很难将错误正确地返回给原始调用者,特别是如果异步逻辑最终变得复杂(使用嵌套或序列操作)。
  3. 仔细考虑您的异步操作是否必须是序列或它们是否可以并行运行。 如果一个操作不依赖于另一个操作,并且您不太可能使多个请求重载某些服务,那么并行运行通常会更快地实现结果。
  4. 从异步函数返回promise,这样调用者就可以知道事情何时完成,并且可以访问异步结果。
  5. 不要不必要地围绕现有的承诺创建另一个承诺(被认为是承诺反模式之一)。
  6. 如果使用jQuery promises,请尝试坚持与promise标准兼容的jQueryfunction,这样您就不会遇到未来的互操作性问题,或者混淆代码的未来读者,他们更有可能了解标准承诺的工作方式。

鉴于这一切,这里有五种方法来实现您的代码 – 使用标准承诺,使用jQuery承诺和您的操作序列或并行运行并使用Bluebird承诺。 在所有情况下,您最终都会按顺序获得一系列结果。

使用标准承诺Promisify readFile()

首先,让我们“宣传”你的readFile操作,这样你就可以使用promise逻辑来控制事物了。

 function readFile(file) { return new Promise(function(resolve, reject) { var reader = new FileReader(); reader.onload = function(e) { resolve(e.target.result); }; reader.onerror = reader.onabort = reject; reader.readAsBinaryString(file); }); } 

有了标准承诺,所有操作都是并行的

要并行运行所有文件操作并按顺序返回所有结果并使用标准promise,您可以这样做:

 function readmultifiles(files) { return Promise.all(files.map(readFile)); } // sample usage readmultifiles(arrayOfFiles).then(function(results) { // all results in the results array here }); 

有了标准承诺,所有操作都按顺序进行

要按顺序运行所有文件操作(这看起来不像你需要在这里执行因为所有操作都是独立的,即使你的原始代码对它们进行排序)并按顺序返回所有结果并使用标准的promises,你可以做这个。

这个有点标准的排序设计模式使用.reduce()来排序整个数组并将所有操作链接在一起,这样它们就可以按照链的顺序一次运行一个:

 function readmultifiles(files) { var results = []; files.reduce(function(p, file) { return p.then(function() { return readFile(file).then(function(data) { // put this result into the results array results.push(data); }); }); }, Promise.resolve()).then(function() { // make final resolved value be the results array return results; }); } // sample usage readmultifiles(arrayOfFiles).then(function(results) { // all results in the results array here }); 

而且,这是使用jQuery承诺的样子

使用jQuery承诺Promisify readFile()

 function readFile(file) { return new $.Deferred(function(def) { var reader = new FileReader(); reader.onload = function() { def.resolve(e.target.result); }; reader.onerror = reader.onabort = def.reject; reader.readAsBinaryString(file); }).promise(); } 

与jQuery并行运行:

 function readmultifiles(files) { return $.when.apply($, files.map(readFile)); } // sample usage readmultifiles(arrayOfFiles).then(function() { var results = Array.prototype.slice(arguments); // all results in the results array here }); 

并且,与jQuery顺序运行

 function readmultifiles(files) { var results = []; files.reduce(function(p, file) { return p.then(function() { return readFile(file).then(function(data) { // put this result into the results array results.push(data); }); }); }, $.Deferred().resolve()).then(function() { // make final resolved value be the results array return results; }); } // sample usage readmultifiles(arrayOfFiles).then(function(results) { // all results in the results array here }); 

蓝鸟实施

而且,为了完整起见,我将向您展示使用像Bluebird这样的更高级的承诺库 ,它具有在这里有用的其他function。 并行代码和readFile()的实现与标准promises相同,但对于顺序实现,它可以利用一些内置的Bluebird操作来对异步操作进行排序,它只包括:

 function readmultifiles(files) { return Promise.mapSeries(files, readFile); } // sample usage readmultifiles(arrayOfFiles).then(function(results) { // all results in the results array here }); 

如果我将代码结构更改为此怎么办?

  $.when(readmultifiles(files)).then( function(status) { alert(status + ", things are going well"); }, function(status) { alert(status + ", you fail this time"); }, function(status) { $("body").append(status); } ); function readmultifiles(files) { var dfrd = $.Deferred(); // Read first file setup_reader(files, 0); function setup_reader(files, i) { var file = files[i]; var name = file.name; var reader = new FileReader(); reader.onload = function(e) { readerLoaded(e, files, i, name); }; reader.readAsBinaryString(file); // After reading, read the next file. } function readerLoaded(e, files, i, name) { // get file content var bin = e.target.result; // do sth with text namee.push(name); // If there's a file left to load if (i < files.length - 1) { // Load the next file setup_reader(files, i + 1); } else { dfrd.resolve(namee.join(',')); } } return dfrd.promise(); }