在循环中将 Promise 与 fs.readFile 一起使用 [英] Using Promises with fs.readFile in a loop

查看:15
本文介绍了在循环中将 Promise 与 fs.readFile 一起使用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图理解为什么下面的 Promise 设置不起作用.

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

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

(Note: I already solved this issue with async.map. But I would like to learn why my attempts below didn't work.)

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

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".

谢谢!

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

Attempt 1: It runs and stops at 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");
}

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

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

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

提前感谢您的帮助!

推荐答案

所以,每当你有多个异步操作需要以某种方式协调时,我都会立即转到 promises.并且,使用 promise 来协调多个异步操作的最佳方式是让每个异步操作返回一个 promise.您显示的最低级异步操作是 fs.readFile().由于我使用 Bluebird 承诺库,它具有承诺"整个模块的异步函数价值的功能.

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

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

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.

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

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

然后,在您的代码中,您似乎想让 bFunc() 成为一个函数,该函数读取其中三个图像并在它们出现时调用 cFunc()完毕.你可以这样做:

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

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

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

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

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

不过,您很快就会发现,一旦开始使用 promise,您就希望将它们用于所有异步操作,因此您将承诺"很多东西,并拥有一个库或至少一个通用函数可以做到这一点因为你会节省很多时间.

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.

甚至在较新版本的 node.js(版本 10.0+)中,您可以使用支持承诺的 fs 库的内置版本:

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

这篇关于在循环中将 Promise 与 fs.readFile 一起使用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆