2012-02-25 7 views
16

単純なリクエストハンドラを作成して、CSSファイルのペアを返します。 fs.readFileSyncを使うと簡単でした。しかし、私はreadFileの非同期バージョンを使用して同じタスクを達成するのが難しいです。以下は私のコードです。私のresponse.write()メソッドコールが2つの異なるコールバックの間でsplitを呼び出すことに問題があるようです。誰かが間違ったことを指摘できますか?興味深いことに、このコードは、最初のelse文の中にresponse.end()を入れると機能します。しかし、これは、(response.end()がすでに起動されているため)、2番目のcssファイルが返されないという問題を引き起こします。fs.readFileを使用してNode.js内の複数のファイルを読み込んで返す

function css(response) { 

    response.writeHead(200, {"Content-Type": "text/css"}); 

    fs.readFile('css/bootstrap.css', function(error, content){ 
    if(error){ 
     console.log(error); 
    } 
    else{ 
     response.write(content); 
    } 
    }); 
    fs.readFile('css/bootstrap-responsive.css', function(error, content){ 
    if(error){ 
     console.log(error); 
    } 
    else{ 
     response.write(content) 
    } 
    }); 
    response.end(); 
} 
+0

したがって、2つのCSSファイルを1つの応答に追加しようとしていますか? – loganfsmyth

+0

はい、そうです。 – hughesdan

+0

誰かがリクエストするたびにファイルを読む必要があるのはなぜですか?あなたはそれらが頻繁に変わることを期待していますか?そうでない場合は、サーバが起動したときにメモリに読み込んで、必要なときに戻ってください。より速く動作します。 – Evgeny

答えて

26

あなたが持っている主な問題は、response.end()がすぐに呼び出されることです。ファイルがresponse.write呼び出しを完了した後にのみ呼び出す必要があります。

最も簡単な方法は、コントロールフローライブラリを使用することです。複数の非同期コールバックの管理は、一般的に複雑です。

https://github.com/joyent/node/wiki/modules#wiki-async-flow

私はそれが私が一番知っている一つだのでasyncライブラリを使用するつもりです。

var fs = require('fs'); 
var async = require('async'); 

function css(response) { 
    response.writeHead(200, {"Content-Type": "text/css"}); 

    async.eachSeries(
    // Pass items to iterate over 
    ['css/bootstrap.css', 'css/bootstrap-responsive.css'], 
    // Pass iterator function that is called for each item 
    function(filename, cb) { 
     fs.readFile(filename, function(err, content) { 
     if (!err) { 
      response.write(content); 
     } 

     // Calling cb makes it go to the next item. 
     cb(err); 
     }); 
    }, 
    // Final callback after each item has been iterated over. 
    function(err) { 
     response.end() 
    } 
); 
} 

ライブラリなしでこれを達成したい場合、または別の方法が必要な場合は、これをより直接的に行う方法です。基本的にはcountを保存し、両方のファイルの読み込みが完了したらendに電話してください。

function css(response) { 
    response.writeHead(200, {"Content-Type": "text/css"}); 

    var count = 0; 
    var handler = function(error, content){ 
    count++; 
    if (error){ 
     console.log(error); 
    } 
    else{ 
     response.write(content); 
    } 

    if (count == 2) { 
     response.end(); 
    } 
    } 

    fs.readFile('css/bootstrap.css', handler); 
    fs.readFile('css/bootstrap-responsive.css', handler); 
} 
+1

ありがとうございます。それは非常に有益な答えでした! – hughesdan

+0

関数cssを呼び出す特定の場所? – Winnemucca

+0

コールバックの内部からhttps://nodejs.org/api/http.html#http_http_createserver_requestlistenerまたはNodeのAPIサーバーパッケージのいずれかに移動します。 – loganfsmyth

1

1つのコールバックですべてを取得する単純な共通の解決策があります。 プロジェクトのどこにでも配置して、さまざまなケースで再利用できます。

var FS = require('fs'); 

/** 
* Abstract helper to asyncly read a bulk of files 
* Note that `cb` will receive an array of errors for each file as an array of files data 
* Keys in resulting arrays will be the same as in `paths` 
* 
* @param {Array} paths - file paths array 
* @param {Function} cb 
* @param {Array} errors - a list of file reading error 
* @param {Array} data - a list of file content data 
*/ 
function FS_readFiles (paths, cb) { 
    var result = [], errors = [], l = paths.length; 
    paths.forEach(function (path, k) { 

     FS.readFile(path, function (err, data) { 
      // decrease waiting files 
      --l; 
      // just skip non-npm packages and decrease valid files count 
      err && (errors[k] = err); 
      !err && (result[k] = data); 
      // invoke cb if all read 
      !l && cb (errors.length? errors : undef, result); 
     }); 

    }); 
} 

ただ、その中のファイルの大部分を入れて、

それ意志あなたに戻り、バッファ、それらのそれぞれ。 簡単な例:

var cssFiles = [ 
    'css/bootstrap.css', 
    'css/bootstrap-responsive.css' 
]; 

function css(response) { 
    FS_readFiles(cssFiles, function (errors, data) { 
     response.writeHead(200, {"Content-Type": "text/css"}); 
     data.forEach(function (v) { 
      response.write(v); 
     }); 
     response.end(); 
    }); 
} 

Offtopicは:ところで、あなたはより良いnginxのか、ニスのようなフロントエンドプロキシサーバーにキャッシュするために、次のように要求します。それは変わらない。

-1

非同期は素晴らしいライブラリです。しかし、これらの標準は、複数の非同期操作を処理するという約束の方向に向かっています。実際にはECMAScript6 this will be a standard part of the libraryにあります。 JQueryを含む約束を実装するライブラリがいくつかあります。しかし、ノードのために、私は使用したい'q'

ここでは約束を使用して同じコードです:1つのメモ..最初の成功した読み取りと一致するように最初のwriteHead呼び出しを移動することがあります。

var Q = require('q'); 

function css(response) { 
    response.writeHead(200, {"Content-Type": "text/css"}); 
    var defer = Q.defer(); 
    fs.readFile('css/bootstrap.css', function(error, content){ 
     if(error){ 

      defer.reject(error) 
     } 
     else{ 
     response.write(content); 
     defer.resolve(); 
     } 
    }); 
    defer.promise.then(function() { //this gets executed when the first read succeeds and is written 
     var secondDefer = Q.defer(); 
     fs.readFile('css/bootstrap-responsive.css', function(error, content){ 
      if(error){ 
       secondDefer.reject(error); 
      } 
      else{ 
       response.write(content); 
       secondDefer.resolve(); 
      } 
     }); 
     return secondDefer.promise; 
    }, 
    function(error) { //this gets called when the first read fails 
     console.log(error); 
     //other error handling 
    }). 
    done(function() { 
     response.end(); 
    }, 
    function(error) { //this is the error handler for the second read fails 
     console.log(error); 
     response.end(); //gotta call end anyway 
    }); 

} 
0

あなたは単にhtml5プロミスに頼ることができます。コードは次のように単純になります。

var promises= ['file1.css', 'file2.css'].map(function(_path){ 
    return new Promise(function(_path, resolve, reject){ 
     fs.readFile(_path, 'utf8', function(err, data){ 
      if(err){ 
       console.log(err); 
       resolve(""); //following the same code flow 
      }else{ 
       resolve(data); 
      } 
     }); 
    }.bind(this, _path)); 
}); 

Promise.all(promises).then(function(results){ 
    //Put your callback logic here 
    response.writeHead(200, {"Content-Type": "text/css"}); 
    results.forEach(function(content){response.write(content)}); 
    response.end(); 
}); 
関連する問題