我试图理解为什么下面的承诺设置不起作用.
(注意:我已经用async.map解决了这个问题.但是我想知道为什么我的下面的尝试不起作用.)
正确的行为应该是:bFunc应该运行尽可能多的时间来读取所有图像文件(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"); }
我在这里先向您的帮助表示感谢!
所以,只要你有多个异步操作以某种方式进行协调,我立即想要去承诺.并且,使用promises协调大量异步操作的最佳方法是使每个异步操作返回一个promise.您显示的最低级别异步操作是fs.readFile()
.由于我使用Bluebird promise库,它具有"宣传"整个模块的异步函数的功能.
var Promise = require('bluebird'); var fs = Promise.promisifyAll(require('fs'));
这将在fs
对象上创建新的并行方法,其后缀为"Async",返回promises而不是使用直接回调.所以,会有一个fs.readFileAsync()
返回一个承诺.你可以在这里阅读更多关于蓝鸟的宣传的信息.
所以,现在你可以创建一个简单地获取图像的函数,并返回一个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,你想要将它们用于所有异步操作,这样你就可以"宣传"很多东西,拥有一个库或至少一个通用函数来为你做这件事.节省大量时间.
Node v10具有Experimental 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
您的代码看起来应该更像这样:
// 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一个功能,就是要把有回调的语义异步函数,并承诺语义从中获得新的功能.
它可以手动完成,如上所示,或者 - 最好 - 自动完成.其中,Bluebird promise库有一个帮手,请参阅http://bluebirdjs.com/docs/api/promisification.html