node.js 在循环中将 Promise 与 fs.readFile 一起使用

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/34628305/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 19:44:09  来源:igfitidea点击:

Using Promises with fs.readFile in a loop

node.jspromisereadfile

提问by David

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 below didn't work.)

(注意:我已经用 async.map 解决了这个问题。但我想知道为什么我在下面的尝试没有奏效。)

The correct behavior should be: bFunc should run as many time as necessary to fs read all the image files (bFunc below runs twice) and then cFunc console prints "End".

正确的行为应该是:bFunc 应该尽可能多地运行 fs 读取所有图像文件(下面的 bFunc 运行两次),然后 cFunc 控制台打印“End”。

Thanks!

谢谢!

Attempt 1: It runs and stops at cFunc().

尝试 1:它在 cFunc() 处运行并停止。

var fs = require('fs');

bFunc(0)
.then(function(){ cFunc() }) //cFunc() doesn't run

function bFunc(i){
    return new Promise(function(resolve,reject){

        var imgPath = __dirname + "/image1" + i + ".png";

        fs.readFile(imgPath, function(err, imagebuffer){

            if (err) throw err;
            console.log(i)

            if (i<1) {
                i++;
                return bFunc(i);
            } else {
                resolve();
            };

        });

    })
}

function cFunc(){
    console.log("End");
}

Attempt 2: In this case, I used a for-loop but it executes out of order. Console prints: End, bFunc done, bFunc done

尝试 2:在这种情况下,我使用了一个 for 循环,但它执行无序。控制台打印:结束,bFunc 完成,bFunc 完成

var fs = require('fs');

bFunc()
        .then(function(){ cFunc() })

function bFunc(){
    return new Promise(function(resolve,reject){

        function read(filepath) {
            fs.readFile(filepath, function(err, imagebuffer){
                if (err) throw err;
                console.log("bFunc done")
            });
        }

        for (var i=0; i<2; i++){
            var imgPath = __dirname + "/image1" + i + ".png";
            read(imgPath);
        };

        resolve()
    });
}


function cFunc(){
    console.log("End");
}

Thanks for the help in advance!

我在这里先向您的帮助表示感谢!

回答by jfriend00

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.

因此,无论何时您有多个异步操作需要以某种方式进行协调,我都会立即使用 Promise。并且,使用 promise 来协调多个异步操作的最佳方式是让每个异步操作返回一个 promise。您显示的最低级别异步操作是fs.readFile(). 由于我使用 Bluebird 承诺库,它具有“承诺”整个模块的异步功能价值的功能。

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

This will create new parallel methods on the fsobject 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.

这将在fs带有“Async”后缀的对象上创建新的并行方法,返回承诺而不是使用直接回调。所以,会有一个fs.readFileAsync()返回承诺的。您可以在此处阅读有关 Bluebird 承诺的更多信息。

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:

因此,现在您可以创建一个函数,该函数可以相当简单地获取图像并返回一个 Promise,其值为图像中的数据:

 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:

然后,在您的代码中,您似乎希望bFunc()成为一个函数,该函数读取其中三个图像并cFunc()在完成后调用。你可以这样做:

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:

如果你不想使用 Bluebird,你可以手动制作一个fs.readFile()像这样的承诺版本:

// 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:

或者,在现代版本的 node.js 中,您可以使用util.promisify()遵循 node.js 异步调用约定的函数的promisified 版本:

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.

但是,您很快就会发现,一旦开始使用 Promise,您就希望将它们用于所有异步操作,因此您将“承诺”很多东西,并且拥有一个库或至少一个通用函数可以为您做到这一点节省大量时间。



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

在 node.js 的更新版本(版本 10.0+)中,您可以使用fs支持 promises的库的内置版本:

const fsp = require('fs').promises;

fsp.readFile("someFile").then(data => {
    console.log(data);
});

回答by Dmitry Yudakov

Node v10 has fs Promises API

Node v10 有 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

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)

回答by Tomalak

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 promisifya function means to take an asynchronous function with callback semantics and derive from it a new function with promise semantics.

promisify功能手段,采取与回调语义异步函数,并从中获得了所应许语义的新功能。

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

它可以手动完成,如上所示,或者 - 最好 - 自动完成。其中,Bluebird 承诺库有一个帮助程序,请参阅http://bluebirdjs.com/docs/api/promisification.html

回答by Changyuan Chen

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

你也可以使用这个模块:'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)
})