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

时间:2017-01-28 05:37:21

标签: javascript jquery nested promise deferred

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

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

2 个答案:

答案 0 :(得分:5)

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

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

    使用标准承诺宣传readFile()

    首先,让我们&#34;宣传&#34;您的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);
        });
    }
    

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

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

    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 promises看起来的样子

    使用jQuery承诺宣传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
    });
    

    蓝鸟实施

    而且,为了完整起见,我将使用一个具有其他功能的more advanced promise library like Bluebird来展示它的外观。并行代码和readFile()的实现与标准promise相同,但对于顺序实现,它可以利用一些内置的Bluebird操作来对异步操作进行排序,它只包括:

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

答案 1 :(得分:0)

  

如果我将代码结构更改为此

,该怎么办?
 $.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();
      }