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

How to determine that all the files have been read and resolve a promise

以下代码负责读取文件。我的要求是如何查找是否已读取所有文件,以便我可以 return 或解决父函数 (readmultifiles) 的承诺。

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

Above code initiates the file read. What can be done so that upon reading of all files callback is done or a return can be made.

        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);
            }
        }

What if I change the code structure to this

 $.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();
      }

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

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

考虑到所有这些,这里有五种方法来实现您的代码 - 使用标准承诺、使用 jQuery 承诺和您的操作序列或 运行 并行和使用 Bluebird 承诺。在所有情况下,最后都会按顺序得到一组结果。

Promisify readFile() 使用标准承诺

首先,我们 "promisify" 您的 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);
    });
}

使用标准承诺,所有操作并行

要运行并行所有文件操作并return按顺序排列所有结果并使用标准承诺,您可以这样做:

function readmultifiles(files) {
    return Promise.all(files.map(readFile));
}

// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
    // all results in the results array here
});

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

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

这种用于排序的标准设计模式使用 .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 promises

的样子

Promisify readFile() 使用 jQuery 承诺:

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
});

蓝鸟实施

并且,为了完整起见,我将使用一些 more advanced promise library like Bluebird 向您展示它的外观,它具有在这里很有用的附加功能。 readFile() 的并行代码和实现与标准承诺相同,但对于顺序实现,它可以利用一些 built-in Bluebird 操作来对异步操作进行排序,它只包括:

function readmultifiles(files) {
    return Promise.mapSeries(files, readFile);
}

// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
    // all results in the results array here
});