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

Kap*_*ath 1 javascript jquery nested promise deferred

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

        $.when(readmultifiles(files))
               .then(function(){//all files uploaded}))
Run Code Online (Sandbox Code Playgroud)

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

        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);
            }
        }
Run Code Online (Sandbox Code Playgroud)

jfr*_*d00 5

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

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

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

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);
    });
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

// sample usage
readmultifiles(arrayOfFiles).then(function(results) {
    // all results in the results array here
});
Run Code Online (Sandbox Code Playgroud)

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

要按顺序运行所有文件操作(这看起来不像你需要在这里执行,因为所有操作都是独立的,即使你的原始代码对它们进行排序)并按顺序返回所有结果并使用标准的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
});
Run Code Online (Sandbox Code Playgroud)

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

readFile()使用jQuery承诺Promisify :

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();
}
Run Code Online (Sandbox Code Playgroud)

与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
});
Run Code Online (Sandbox Code Playgroud)

并且,与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
});
Run Code Online (Sandbox Code Playgroud)

蓝鸟实施

而且,为了完整起见,我将向您展示使用像Bluebird这样的更高级的承诺库,它具有在这里有用的附加功能.并行代码和实现与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
});
Run Code Online (Sandbox Code Playgroud)

  • 哇。就像阅读 js 圣经一样。这些东西对我来说都是新的。谢谢你开门。 (2认同)