私は、以下の約束のセットアップが機能しない理由を理解しようとしています。
(注:この問題は既にasync.mapで解決しています。しかし、以下の試みがうまくいかなかった理由を知りたいです。)
正しい動作は次のとおりです。bFuncは、すべてのイメージファイルを読み取るために必要な回数実行し(以下のbFuncが2回実行されます)、cFuncコンソールが「End」を出力します。
ありがとう!
試行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");
}
事前に助けてくれてありがとう!
したがって、何らかの方法で調整する複数の非同期操作がある場合は、すぐに約束に行きたいと思います。そして、promiseを使用して多くの非同期操作を調整する最良の方法は、各非同期操作がpromiseを返すようにすることです。表示する最低レベルの非同期操作はfs.readFile()
です。私はBluebirdのpromiseライブラリを使用しているため、モジュール全体の非同期機能を「約束」する機能があります。
_var Promise = require('bluebird');
var fs = Promise.promisifyAll(require('fs'));
_
これにより、fs
オブジェクトに新しい非同期メソッドが作成され、サフィックスが「非同期」になり、ストレートコールバックを使用する代わりにプロミスが返されます。したがって、promiseを返すfs.readFileAsync()
があります。 Bluebirdの約束について詳しく読むことができます こちら 。
したがって、画像をかなり簡単に取得し、その値が画像のデータであるプロミスを返す関数を作成できます。
_ function getImage(index) {
var imgPath = __dirname + "/image1" + index + ".png";
return fs.readFileAsync(imgPath);
}
_
次に、コードで、bFunc()
をこれらのイメージのうち3つを読み取り、完了時に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()
のpromiseバージョンを手動で作成できます。
_// 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非同期呼び出し規約に従う関数の約束されたバージョンを作成できます。
_const util = require('util');
fs.readFileAsync = util.promisify(fs.readFile);
_
ただし、Promiseの使用を開始したら、すべての非同期操作にそれらを使用して、多くのことを「約束」し、ライブラリまたは少なくともそれを行う汎用関数を用意することがすぐにわかります。多くの時間を節約できます。
Node.jsのさらに新しいバージョン(バージョン10.0+)では、promiseをサポートするfs
ライブラリの組み込みバージョンを使用できます。
_const fsp = require('fs').promises;
fsp.readFile("someFile").then(data => {
console.log(data);
});
_
コードは次のようになります。
// 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 を参照してください
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
または、さらに多くのファイルを同時に読みたい場合:
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)
このモジュールを使用することもできます: '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)
})