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

前端 未结 2 376
南笙
南笙 2021-01-25 08:58

The following code is responsible for reading files. My requirement is how to find whether all files has been read so that I can return or resolve a promise from the parent fun

2条回答
  •  醉话见心
    2021-01-25 09:56

    There are several things to consider in a good design using promises that your implementation could learn from:

    1. Create a promise (called "promisify") from the lowest level async operation your have. Then, you can use promise features to control the logic flow and propagate errors and your code will be consistently implemented with promises. In this case, it means you should promisify readFile(). It also makes readFile() more useful elsewhere in your project or in future projects.
    2. Make sure you are always propagating errors properly. With async code when not using promises, it can be hard to properly get errors back to the original caller, particular if the async logic ends up complicated (with nested or sequences operations).
    3. Consider carefully whether your async operations must be sequences or whether they can run in parallel. If one operation does not depend upon another and you aren't likely to overload some service with multiple requests, then running things in parallel will often achieve a result quicker.
    4. Return promises from async functions so callers can know when things are done and can access async results.
    5. Don't create another promise around an existing promise unnecessarily (considered one of the promise anti-patterns).
    6. If using jQuery promises, try to stick to jQuery features that are compatible with the promise standard so you don't run into interoperability issues going forward or confuse future readers of your code who are more likely to know how standard promises work.

    Given all that, here are five ways to implement your code -using standard promises, using jQuery promises and with your operation sequences or run in parallel and using Bluebird promises. In all cases, you get an array of results in order at the end.

    Promisify readFile() using standard promises

    First, let's "promisify" your readFile operation so you can then use promise logic to control things.

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

    With standard promises, all operation in parallel

    To run all your file operations in parallel and return all the results in order and use standard promises, you can do this:

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

    With standard promises, all operations in sequence

    To run all your files operations in sequence (which it does not look like you need to do here because all the operations are indepedent even though your original code was sequencing them) and return all the results in order and use standard promises, you can do this.

    This, somewhat standard design pattern for sequencing uses .reduce() to sequence through the array and chain all the operations together so they are run one at a time down the sequence of the chain:

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

    And, here's how it would look using jQuery promises

    Promisify readFile() using jQuery promises:

    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 in a parallel with 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
    });
    

    And, to run in sequence with 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 implementation

    And, for completeness, I'll show you what it looks like using a little more advanced promise library like Bluebird that has additional capabilities that are useful here. The parallel code and the implementation of readFile() is the same as for standard promises, but for the sequential implementation, it could take advantage of some built-in Bluebird operations for sequencing async operation and it would just consist of:

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

提交回复
热议问题