2016-11-28 6 views
1

エラーロギングを伴うパイプラインの処理を処理するための慣用的な機能的方法を提案できます。サンプルの必須スタイル(JavaScriptの場合):エラーログの処理パイプラインを処理するための慣用的な機能的な方法は何ですか?

const filesToProcess = ['file1.txt','file2.txt','non_existent_file.txt']; 

var totalLetterCountImperative = 0; 
for (var i = 0; i < filesToProcess.length; i++){ 
    try { 
     totalLetterCountImperative += fs.readFileSync(filesToProcess[i],'utf8').length; 
    } catch (e) { 
     console.log("There is an error whilst processing file: " + filesToProcess[i] + ". Hence, it's skipped. Error: " + e.message); 
    } 
} 
console.log("Total Letter Count: " + totalLetterCountImperative); 

以下の試みはうまくいくが、やっかいで扱いにくいようだ。また、それはすべてのエラーへの一般的ではありません。

const filesToProcess = ['file1.txt','file2.txt','non_existent_file.txt']; 

const totalLetterCount = filesToProcess 
          .filter(f => fs.existsSync(f)) 
          .map(f => fs.readFileSync(f,'utf8').length) 
          .reduce((a,b) => a+b); 

filesToProcess 
    .filter(f => !fs.existsSync(f)) 
    .map(f => console.error("There is an error whilst processing file: " + f +". Hence it's skipped. Error: File doesn't exist")); 

console.log("Total Letter Count: " + totalLetterCount); 

私はEitherの使用について読みました。それが実際に慣用的な方法であれば、誰かが例を挙げ、使用するには良いJavaScriptライブラリを提案できますか?

ありがとうございました。

+1

あなたは、エラー時にパイプラインを放棄 'Either'モナドを使用しますが、あなただけのエラーをログに記録が、パイプラインを継続したい場合は、' Writer'モナドを使用したい場合 。すばらしいGoogle検索で、両方のJSライブラリが見つかりました。 – 4castle

+1

これのためにモナドは必要ないかもしれません。 [検証](https://github.com/folktale/data.validation)申請者を調べてください。これは 'Either'と同様に動作しますが、短絡がなく、エラー処理を目的とした語彙があります。 – ftor

答えて

0

処理の各ステップでは、「文字カウント」または「コンソールメッセージ」のいずれかの結果が生成されます。私の意見では、しかし

const filesToProcess = ['file1.txt','file2.txt','non_existent_file.txt']; 

var results = filesToProcess.map(function(f){ 
    try { 
     var n = readFileSync(filesToProcess[i],'utf8').length; 
     return {tag:"count", value: n}; 
    } catch (e) { 
     var msg = "There is an error whilst processing file: " + filesToProcess[i] + ". Hence, it's skipped. Error: " + e.message); 
     return {tag:"err", value:msg}; 
    } 
}) 

var totalLetterCount = 
    results 
     .filter(r => r.tag === "count") 
     .map(r => r.value) 
     .reduce((a,b)=> a + b); 

results 
    .filter(r => t.tag == "error") 
    .map(r => r.value) 
    .map(msg => console.error(msg)); 

:この問題に取り組むための最も簡単で最も直接的な方法の一つは、あなたの最初のパスは、これらの値の異質リストを作成しており、2つの均質なリストにそれらを分離するために、第2のパスを行うことですより良い解決策は、副作用を利用して、あなたが既に必須スニペットでやっているように、console.errorを使うことかもしれません。このようにすれば、物事を合算してエラーを記録するというクロスカッティングの問題を混在させる必要はなく、「純粋に機能的」なソリューションを主張した場合に得られるよりも似通ったものになります。

私はOcamlでプログラミングしているときにこのようなことをしています。複雑なモナドやモナド変換子を使用するためにすべてをリファクタリングするよりも、ログに値を追加するために副作用を使用するほうがはるかに簡単です。

0

//これはRamdaJSと民話

const fs = require('fs'); 
const R = require('ramda'); 
const Result = require('folktale/result'); 

const filesToProcess = ['1.txt','2.txt','non_existent_file.txt']; 

const processFile = function(file){ 
    try { 
     const count = fs.readFileSync(file,'utf8').length; 
     return Result.Ok(count); 
    } catch (e) { 
     return Result.Error(`Error whilst processing ${file}. It's skipped.\n`); 
    } 
}; 

const splitEithers = R.groupBy(el => (
    Result.Error.hasInstance(el)) 
     ? 'errors' 
     : 'counts' 
); 

const doMath = (processedFiles) => { 
    const totalCount = 
     processedFiles 
      .counts 
      .reduce((n, okNumber) => R.add(n, okNumber.getOrElse(0)), 0); 

    const errors = 
     processedFiles 
      .errors 
      .reduce((msg, errorMessage) => 
       msg + errorMessage.matchWith({Error: n => n.value})); 

    return [errors, totalCount]; 
}; 

const FX = ([errors, count]) => { 
    console.log(`Total word count: ${count}`); 
    console.log(errors); 
}; 

const main = R.pipe(
    R.map(processFile), 
    splitEithers, 
    doMath, 
    FX 
); 

main(filesToProcess); 
0

を使用して、私の実装だろうここだけのネイティブモジュールを使用して慣用ES6のアプローチです:

const fs = require('fs') 
const { promisify } = require('util') 
const access = promisify(fs.access) 
const readFile = promisify(fs.readFile) 

const filesToProcess = ['file1.txt', 'file2.txt', 'non_existent_file.txt'] 

Promise.all(filesToProcess.map(file => access(file, fs.constants.R_OK) 
    .then(() => readFile(file, 'utf8')) 
    .catch(error => { 
    console.log(`There is an error whilst processing file: ${file}. Hence, it's skipped. Error: ${error.message}`) 

    return { length: 0 } 
    }) 
)).then(files => { 
    const totalLetterCount = files.reduce((a, b) => a.length + b.length, 0) 

    console.log(`Total Letter Count: ${totalLetterCount}`) 
}) 

util.promisify()あなたは非同期にコールバックスタイルの機能を変換することができます関数(換言すれば、Promiseを返す関数)を使用します。これにより、スレッドが他のタスクを同時に実行できるように、同期関数の使用を避け、バックグラウンドプロセスを待っているときにスレッドの制御を解放することができます。

さらに、fs.exists()は現在非推奨となっているため、代わりにの使用を選択しました。

非常に軽微だった最後の変更は、予想通りlength 1のアレイが依然として適切に低減するであろうように、代わりにそのように最初の要素を返すので、Array#reduce()0のオプションの引数を供給するだけだった。

const totalLength = [{ length: 5 }].reduce((a, b) => a.length + b.length) 
 
// since we didn't supply the optional argument, we didn't get `5` like we expected 
 
console.log(totalLength)

関連する問題