2017-01-10 11 views
0

シンプルな同期Node.jsプログラムをasync/awaitを使用する非同期バージョンに変換する方法を示す例をまとめます。通常のコールバックベースのバージョンから、2つのコールバックを使用するコールバック、通常(解決)の場合とエラー(拒否)の場合の2つの中間ステップから、約束につながるいくつかの中間ステップが必要です。ノードスタイルのコールバックを2つのコールバックに分割する

各バージョンのジョブが(すでに存在している可能性があり、それはファイルが含まれているかもしれない)空のフォルダコピーを作成し、フォルダ内のすべてのファイル( FILE2.TXT FILE1.TXTと呼ばれる)をコピーすることですorigがあります。どこにでもエラーが発生した場合は、明示的にキャッチしてコンソールに出力し、プログラムをそれ以上続行しないでください。

通常のエラーファーストコールバックのバージョンは正常ですが、スプリットコールバックバージョンの問題が発生しました。 file2.txtのみをコピーしますが、file1.txtはコピーしません。

const fs = require('fs'); 

fs.exists = function(path, callback) { 
    fs.stat(path, (err, stats) => { 
     if (err) { 
      callback(null, false); 
     } else { 
      callback(null, true); 
     } 
    }); 
}; 

function splitCallback(f) { 
    return (...params) => { 
     reject = params[params.length - 2]; 
     resolve = params[params.length - 1]; 
     params = params.slice(0, params.length - 2); 
     f(...params, (err, data) => { 
      if (err) { 
       reject(err); 
      } else { 
       resolve(data); 
      } 
     }); 
    }; 
} 

const sfs = {}; 
const functionNames = ['exists', 'readdir', 'unlink', 'mkdir', 'readFile', 'writeFile']; 
for (const functionName of functionNames) { 
    sfs[functionName] = splitCallback(fs[functionName].bind(fs)); 
} 

そして、これは、これらの機能を使用して、実際の例です:

function handleError(err) { 
    console.error(err); 
} 

function initCopyDirectory(callback) { 
    sfs.exists('copy', handleError, exists => { 
     if (exists) { 
      sfs.readdir('copy', handleError, filenames => { 
       let fileCount = filenames.length; 
       if (fileCount === 0) { 
        callback(); 
       } 
       for (const filename of filenames) { 
        sfs.unlink(`copy/${filename}`, handleError,() => { 
         fileCount--; 
         if (fileCount === 0) { 
          callback(); 
         } 
        }); 
       } 
      }); 
     } else { 
      sfs.mkdir('copy', handleError,() => callback); 
     } 
    }); 
} 

function copyFiles() { 
    // sfs.readdir('orig', handleError, filenames => { 
    //  for (const filename of filenames) { 
    //   console.log(filename); 
    //   sfs.readFile(`orig/${filename}`, handleError, data => { 
    //    console.log('reading', filename); 
    //    sfs.writeFile(`copy/${filename}`, data, handleError,() => { 
    //     console.log('writing', filename); 
    //    }); 
    //   }); 
    //  } 
    // }); 
    sfs.readdir('orig', handleError, filenames => { 
     for (const filename of filenames) { 
      fs.readFile(`orig/${filename}`, (err, data) => { 
       if (err) { 
        handleError(err); 
       } else { 
        sfs.writeFile(`copy/${filename}`, data, handleError,() => {}); 
       } 
      }); 
     } 
    }); 
} 

function main() { 
    initCopyDirectory(copyFiles); 
} 

main(); 

それがここに書かれているとして、それが正常に動作します(ここで

は、私はFS-機能を変換するために使用するコードですノードバージョン7.4.0 for Windowsを使用しています)。しかし、copyFiles関数でコメントを入れ替えると(readFileを変更すると)、1つのファイルだけがコピーされ、次の出力が得られます。

file1.txt 
file2.txt 
reading file2.txt 
writing file2.txt 
writing file2.txt 

問題点は何ですか?

答えて

0

はこれを試してみてください代わりに、コメントコードの:

 for (const filename of filenames) { 
     (function(filename){ 
      console.log(filename); 
      sfs.readFile(`orig/${filename}`, handleError, data => { 
       console.log('reading', filename); 
       sfs.writeFile(`copy/${filename}`, data, handleError,() => { 
        console.log('writing', filename); 
       }); 
      }); 
     })(filename) 
    } 

問題は、forループ内で非同期の機能を実行し、それらが同期動作するように期待しているということです。 sfs.writeFileが(sfs.readFileの実行後に)呼び出されるまでに、forループは長い間実行されており、最後のファイル名file2だけが残っています。クロージャの中のforループの中にすべてをラップすることによって、適切な値を維持します。ここ

は簡単の例である:

for (var i = 0; i < 10 ; i++) { 
    setTimeout(function(){ 
     console.log(i) 
    }, 100) 
} 

が原因で時間によって10 10回印刷するforループ既に行われているタイムアウト実行する(0.1秒)、次のコードはを通じて番号0を印刷するのに対し9は元の値がクロージャによって保持されるためです。問題は、私がsplitCallbackでの変数宣言の前でのconstを置くのを忘れていることだった

for (var i = 0; i < 10 ; i++) { 
    (function(i){ 
     setTimeout(function(){ 
      console.log(i) 
     }, 100) 
    })(i) 
} 
+0

これは私が最初に考えたことですが、私はここでも同じ結果が得られます。そして、[this](http://exploringjs.com/es6/ch_for-of.html#_iteration-variables-const-declarations-versus-var-declarations)によれば、私はconst宣言された変数の新しいバインディングをfor-ofループを繰り返します。 – Ignavia

+0

@Ignavia「新鮮な綴り」はどういう意味ですか? constを使用しても、forループ内の非同期関数内のバインディングを保持できないという事実は変わりません。これは、常に非同期関数より速くなります – AllTheTime

+0

少し変更された形式で簡単な例を使用します: 'const a = [1,2,3、 4,5,6,7,8,9,10]; for(var i of a){ setTimeout(function(){ console.log(i); }、100); } 'は出力として10回10を生成し、' const a = [1,2,3,4,5,6,7,8,9,10]です。 for(const i){ setTimeout(function(){ console.log(i); }、100); } 'は1から10までの数字を生成します。最初のコメントのリンクの下にはより良いフォーマットのバージョンがあります。 – Ignavia

0

を(自分でそれを試してみてください)。これにより、グローバル変数がオーバーライドされ続けました。 strictモードを有効にすると、代わりにエラーが発生しました。ここに正しいコードがあります:

function splitCallback(f) { 
    return (...params) => { 
     const input    = params.slice(0, params.length - 2); 
     const [reject, resolve] = params.slice(params.length - 2); 
     f(...input, (err, ...output) => { 
      if (err) { 
       reject(err); 
      } else { 
       resolve(...output); 
      } 
     }); 
    }; 
} 
関連する問題