Using Promises with fs.readFile in a loop

后端 未结 4 2070
野的像风
野的像风 2020-11-29 03:27

I\'m trying to understand why the below promise setups don\'t work.

(Note: I already solved this issue with async.map. But I would like to learn why my attempts belo

相关标签:
4条回答
  • 2020-11-29 03:51

    Node v10 has fs Promises API

    const fsPromises = require('fs').promises
    
    const func = async filenames => {
    
      for(let fn of filenames) {
        let data = await fsPromises.readFile(fn)
      }
    
    }
    
    func(['file1','file2'])
      .then(res => console.log('all read', res))
      .catch(console.log)
    

    https://nodejs.org/api/fs.html#fs_fs_promises_api

    Or if you want to read more files simultaneously:

    const func = filenames => {
      return Promise.all(
        filenames.map(f => fsPromises.readFile(f))
      )
    }
    
    func(['./a','./b'])
      .then(res => console.log('all read', res))
      .catch(console.log)
    
    0 讨论(0)
  • 2020-11-29 03:56

    So, anytime you have multiple async operations to coordinate in some way, I immediately want to go to promises. And, the best way to use promises to coordinate a number of async operations is to make each async operation return a promise. The lowest level async operation you show is fs.readFile(). Since I use the Bluebird promise library, it has a function for "promisifying" a whole module's worth of async functions.

    var Promise = require('bluebird');
    var fs = Promise.promisifyAll(require('fs'));
    

    This will create new parallel methods on the fs object with an "Async" suffix that return promises instead of use straight callbacks. So, there will be an fs.readFileAsync() that returns a promise. You can read more about Bluebird's promisification here.

    So, now you can make a function that gets an image fairly simply and returns a promise whose value is the data from the image:

     function getImage(index) {
         var imgPath = __dirname + "/image1" + index + ".png";
         return fs.readFileAsync(imgPath);
     }
    

    Then, in your code, it looks like you want to make bFunc() be a function that reads three of these images and calls cFunc() when they are done. You can do that like this:

    var Promise = require('bluebird');
    var fs = Promise.promisifyAll(require('fs'));
    
     function getImage(index) {
         var imgPath = __dirname + "/image1" + index + ".png";
         return fs.readFileAsync(imgPath);
     }
    
     function getAllImages() {
        var promises = [];
        // load all images in parallel
        for (var i = 0; i <= 2; i++) {
            promises.push(getImage(i));
        }
        // return promise that is resolved when all images are done loading
        return Promise.all(promises);
     }
    
     getAllImages().then(function(imageArray) {
        // you have an array of image data in imageArray
     }, function(err) {
        // an error occurred
     });
    

    If you did not want to use Bluebird, you could manually make a promise version of fs.readFile() like this:

    // make promise version of fs.readFile()
    fs.readFileAsync = function(filename) {
        return new Promise(function(resolve, reject) {
            fs.readFile(filename, function(err, data){
                if (err) 
                    reject(err); 
                else 
                    resolve(data);
            });
        });
    };
    

    Or, in modern versions of node.js, you can use util.promisify() to make a promisified version of a function that follows the node.js async calling convention:

    const util = require('util');
    fs.readFileAsync = util.promisify(fs.readFile);
    

    Though, you will quickly find that once you start using promises, you want to use them for all async operations so you'll be "promisifying" lots of things and having a library or at least a generic function that will do that for you will save lots of time.


    In even newer versions of node.js (version 10.0+), you can use the built-in version of the fs library that supports promises:

    const fsp = require('fs').promises;
    
    fsp.readFile("someFile").then(data => {
        console.log(data);
    });
    
    0 讨论(0)
  • 2020-11-29 04:01

    Your code should look more like this:

    // promisify fs.readFile()
    fs.readFileAsync = function (filename) {
        return new Promise((resolve, reject) => {
            fs.readFile(filename, (err, buffer) => {
                if (err) reject(err); else resolve(buffer);
            });
        });
    };
    
    const IMG_PATH = "foo";
    
    // utility function
    function getImageByIdAsync(i) {
        return fs.readFileAsync(IMG_PATH + "/image1" + i + ".png");
    }
    

    Usage with a single image:

    getImageByIdAsync(0).then(imgBuffer => {
        console.log(imgBuffer);
    }).catch(err => {
        console.error(err);
    });
    

    Usage with multiple images:

    var images = [1,2,3,4].map(getImageByIdAsync);
    
    Promise.all(images).then(imgBuffers => {
        // all images have loaded
    }).catch(err => {
        console.error(err);
    });
    

    To promisify a function means to take an asynchronous function with callback semantics and derive from it a new function with promise semantics.

    It can be done manually, like shown above, or – preferably – automatically. Among others, the Bluebird promise library has a helper for that, see http://bluebirdjs.com/docs/api/promisification.html

    0 讨论(0)
  • 2020-11-29 04:12

    you can also use this module: 'fs-readfile-promise'

    var readFile = require('fs-readfile-promise');
    readFile(__dirname + '/file1.txt','utf-8').then(function (data){
        console.log("file's name:", data)
        return readFile(__dirname +'/'+data, 'utf-8')
    }).then(function (data1){
        console.log('Content data:', data1)
    }).catch( function (err){
        console.log(err)
    })

    0 讨论(0)
提交回复
热议问题