在循环中与fs.readFile一起使用Promises [英] Using Promises with fs.readFile in a loop
问题描述
我试图了解为什么以下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控制台打印结束".
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");
}
感谢您的提前帮助!
推荐答案
因此,只要您有多个异步操作以某种方式进行协调,我都会立即承诺.并且,使用promise协调许多异步操作的最佳方法是使每个异步操作返回一个promise.您显示的最低级别的异步操作是fs.readFile()
.自从我使用了Bluebird Promise库以来,它就具有一个使"整个模块的异步功能有价值的功能.
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'));
这将在fs
对象上创建带有异步"后缀的新并行方法,该后缀返回承诺而不是使用直接回调.因此,将有一个fs.readFileAsync()
返回一个Promise.您可以在此处了解更多信息.
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()
的Promise版本:
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);
});
这篇关于在循环中与fs.readFile一起使用Promises的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!