あなたは正しいですfs.readFile()
コールバックの非同期性が原因で問題が発生している可能性があります。
これらのコールバックは、残りのコードが引き続き実行されている間、未定義の時刻に呼び出されます。イベント駆動型のnode.js設計のため、残りのコードが実行されるまでコールバックは呼び出されません。したがって、結果が出る前にenglishWords
とfrenchWords
変数を使用しようとしていることが保証されています。
あなたは、さまざまなオプションの束を持っている:あなたはfs.readFileSync()
の使用に切り替えることができます(ほとんどの場合にはお勧めしません)fs.readFileAsyncへ
スイッチ
。現在の制御の流れが働くので、これは最も簡単な変更です。しかし、これは一般にnode.jsの開発にはお勧めできません。これは、サーバの使用に非効率的なためです。このコードが、ファイルが読み込まれるのを待っている間に他のことを行う能力を維持できる/できなければならないサーバープロセスにあった場合、fs.readFileSync()
はスケーラビリティを犠牲にします。これが単なるスクリプト(ロードされたサーバではない)であれば、fs.readFileSync()
はうまくいくかもしれません。しかし、おそらく、非同期操作で適切にコーディングするより良い "node.jsスタイル"の待機を学ぶべきです(次のオプションを参照してください)。
継続フローインサイドによるSerializeを操作コールバック
あなたは、ネストを使用して非同期操作をシリアル化することができます。これには、非同期コールバック内でのみ処理ロジックを継続する必要があります。そうすることで、必要な結果が処理を継続できるようになることがわかります。
const fs = require('fs')
// read the french file
fs.readFile('frenchVerbsList.txt', 'utf8', function (err, data) {
if (err) {
return console.log("ERROR here!: " + err);
}
var frenchWords = data.split('\n');
//read the english file
fs.readFile('englishVerbsList.txt', 'utf8', function (err, data2) {
if (err) {
return console.log("ERROR here!: " + err);
}
var englishWords = data2.split('\n');
// 2. combine the lists
var combinedList = [];
for (i = 0; i < frenchWords.length; i++) {
combinedList[i] = frenchWords[i] + ",,," + englishWords[i];
}
// 3. check the result
for (i = 0; i < 10; i++) {
console.log(combinedList[i]);
}
});
});
手動でコード、それは前に行われた最初の非同期操作を待機することで上記のどちらの非同期操作が完了している
シリアル化オプションは欠点を持っているためのチェック:それはこのようになります。次の非同期操作を開始します。両方の非同期操作が並行して実行される可能性があるため(理想的ではありません)、最終結果が速くなります。以下のオプションはすべて、非同期操作を並行して実行する方法と、両方が終了したときに監視する方法で、最終処理をトリガーすることができます。これは手動監視オプションです。フランス語と英語の両方の読み込みの完了コールバックで、もう一方が完了しているかどうかを確認します。そうであれば、結果を処理する関数を呼び出します。唯一以来1にはエラーがない限り、そのうちの一つが第二完了します、一度に完了することができ、結果を処理するために、あなたの関数を呼び出します:
var frenchWords, englishWords;
fs = require('fs')
// read the french file
fs.readFile('frenchVerbsList.txt', 'utf8', function (err, data) {
if (err) {
return console.log("ERROR here!: " + err);
}
frenchWords = data.split('\n');
if (frenchWords && englishWords) {
processResults();
}
});
//read the english file
fs.readFile('englishVerbsList.txt', 'utf8', function (err, data2) {
if (err) {
return console.log("ERROR here!: " + err);
}
englishWords = data2.split('\n');
if (frenchWords && englishWords) {
processResults();
}
});
function processResults() {
// combine the lists
var combinedList = [];
for (let i = 0; i < frenchWords.length; i++) {
combinedList[i] = frenchWords[i] + ",,," + englishWords[i];
}
// check the result
for (let i = 0; i < 10; i++) {
console.log(combinedList[i]);
}
}
使用ES6があなたの非同期操作を監視することを約束し
ES6では、約束がJavascript仕様の標準部分になり、複数の非同期操作を調整する優れた方法であり、適切なエラー処理(特に複雑な状況で)をより簡単に行うことができます。ここで約束を使用するには、まずfs.readFile()
の「約束された」バージョンを作成したいと考えています。これは、プレーンコールバックの代わりに約束を使用するラッパー関数です。次に、Promise.all()
を使用して、2つの非同期操作がいつ実行されるかを調整できます。
var fs = require('fs');
// promise wrapper
fs.readFileAsync = function(file, encoding) {
return new Promise(function(resolve, reject) {
fs.readFile(file, encoding, function(err, data) {
if (err) return reject(err);
resolve(data);
});
});
}
// common helper function
function readFileSplitWords(file) {
return fs.readFileAsync(file, 'utf8').then(function(data) {
// make split words be the fulfilled value of the promise
return data.split('\n');
});
}
var frenchPromise = readFileSplitWords('frenchVerbsList.text');
var englishPromise = readFileSplitWords('englishVerbsList.txt');
Promise.all([frenchPromise, englishPromise]).then(function(results) {
// combine the lists
var frenchWords = results[0], englishWords = results[1];
var combinedList = [];
for (i = 0; i < frenchWords.length; i++) {
combinedList[i] = frenchWords[i] + ",,," + englishWords[i];
}
// check the result
for (i = 0; i < 10; i++) {
console.log(combinedList[i]);
}
}, function(err) {
// handle an error here
});
は
ES6約束は非常に可能な拡張約束機能の約束ライブラリを使用しますが、一部のサードパーティのライブラリを追加した約束を使用して非常に便利な機能があります。私は個人的にBluebirdライブラリを使用しています。ここでは、前のオプションは、ブルーバードライブラリを使用してどのように見えるかです:
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
// common helper function
function readFileSplitWords(file) {
return fs.readFileAsync(file, 'utf8').then(function(data) {
// make split words be the fulfilled value of the promise
return data.split('\n');
});
}
var frenchPromise = readFileSplitWords('frenchVerbsList.text');
var englishPromise = readFileSplitWords('englishVerbsList.txt');
Promise.all([frenchPromise, englishPromise]).spread(function(frenchWords, englishWords) {
// combine the lists
var combinedList = [];
for (i = 0; i < frenchWords.length; i++) {
combinedList[i] = frenchWords[i] + ",,," + englishWords[i];
}
// check the result
for (i = 0; i < 10; i++) {
console.log(combinedList[i]);
}
}, function(err) {
// handle an error here
});
これは、ブルーバードのPromise.promisifyAll()
が自動的fs
ライブラリ(非常に便利)のすべてのメソッドのpromisifiedバージョンを作るために使用しています。そして、.then()
の代わりに.spread()
メソッドを使用して、2つの結果を名前付き引数に自動的に分離します。
使用より拡張機能は、ファイル名任意の配列を処理する
あなたはまた、そのようなアレイを処理し、次いで得られた約束に
Promise.all()
(上記のコードは、手動で行った何かし
Promise.map()
、より拡張されたブルーバード機能を使用することができ
)。この場合、あなたはその点で、より一般的な行うことができるファイル名は何でもしたい言語やコードのファイル名の任意のリストで行うことができます:それは(
const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'));
// common helper function
function readFileSplitWords(file) {
return fs.readFileAsync(file, 'utf8').then(function(data) {
// make split words be the fulfilled value of the promise
return data.split('\n');
});
}
var files = ['frenchVerbsList.text', 'englishVerbsList.txt'];
Promise.map(files, readFileSplitWords).then(function(results) {
// results is an array of arrays where each sub-array is a language list of words
// combine the lists (assumes all word lists have the same length)
var combinedList = [];
var len = results[0].length;
// for each word in the first array
for (var i = 0; i < len; i++) {
// get all the other words in the same array position
var words = [];
for (var j = 0; j < results.length; j++) {
words.push(results[j][i]);
}
combinedList.push(words.join(',,,'));
}
// check the result
for (i = 0; i < 10; i++) {
console.log(combinedList[i]);
}
}, function(err) {
// handle an error here
});
あなたのループがfs.readFileコールのコールバックの前に実行されています時間がかかります)* frenchWords *と* englishWords *はそこでは定義されていません。関数の非同期バージョンを使用すると、インタープリタはこれらの関数の結果を「待機」せずに次の行(この場合はループ)を解析し、変数に値を割り当てるコールバックが呼び出されたことを確認できません彼らが最初に呼び出される可能性があります!) –