我试图理解为什么下面的承诺设置不起作用。
(注意:我已经用async.map解决了这个问题。但我想了解为什么我的尝试在下面没有用。)
正确的行为应该是:bFunc应该根据需要运行fs以读取所有图像文件(下面的bFunc运行两次),然后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");
}
尝试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");
}
提前感谢您的帮助!
答案 0 :(得分:59)
因此,只要您有多个异步操作以某种方式进行协调,我立即想要去承诺。并且,使用promises协调大量异步操作的最佳方法是使每个异步操作返回一个promise。您显示的最低级别异步操作是fs.readFile()
。由于我使用了Bluebird承诺库,因此它具有&#34; promisifying&#34;整个模块的异步功能。
var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
这将在fs
对象上使用&#34; Async&#34;创建新的并行方法。返回promises而不是使用直接回调的后缀。因此,会有一个fs.readFileAsync()
返回一个承诺。您可以阅读有关Bluebird的promisification here的更多信息。
所以,现在你可以创建一个非常简单地获取图像的函数,并返回一个其值为图像数据的promise:
function getImage(index) {
var imgPath = __dirname + "/image1" + index + ".png";
return fs.readFileAsync(imgPath);
}
然后,在您的代码中,您似乎希望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
});
如果您不想使用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);
});
});
};
或者,在node.js的现代版本中,您可以使用util.promisify()
创建遵循node.js异步调用约定的函数的promisified版本:
const util = require('util');
fs.readFileAsync = util.promisify(fs.readFile);
尽管如此,您很快就会发现,一旦开始使用promises,您就希望将它们用于所有异步操作,这样您就可以实现&#34; promisifying&#34;很多东西,有一个库或至少一个通用的功能,可以为你节省大量的时间。
答案 1 :(得分:20)
您的代码应该更像这样:
// 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");
}
使用单张图片:
getImageByIdAsync(0).then(imgBuffer => {
console.log(imgBuffer);
}).catch(err => {
console.error(err);
});
使用多个图像:
var images = [1,2,3,4].map(getImageByIdAsync);
Promise.all(images).then(imgBuffers => {
// all images have loaded
}).catch(err => {
console.error(err);
});
promisify 函数意味着采用具有回调语义的异步函数,并从中派生出具有promise语义的新函数。
可以手动完成,如上所示,或者 - 最好 - 自动完成。其中,Bluebird promise库有一个帮助,请参阅http://bluebirdjs.com/docs/api/promisification.html
答案 2 :(得分:12)
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'])
答案 3 :(得分:0)
您也可以使用以下模块: '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)
})