async.jsで有名なcaolanさんのNode.js: Style and structureを対訳を掲載してみました。

前回async.jsで有名な、caolanさんのNode.js: Style and structureを日本語訳をしました。コメントに原文も引用符みたいに対訳を載せてもらえるというありがたい意見を頂いたので、のっけてみます。

相変わらずのレベルの低さですが、おかしな部分がありましたら、指摘頂けますと幸いです。m(_ _)m

Node.js: Style and structure

Node.jsのスタイルと構造


This may seem like quibbling over coding style, but in Node.js it's an important way of keeping your functions easily composable and reusable.
This is not a style guide in the usual sense; I'm assuming you already have your opinions on JavaScript indentation and all the other religious standpoints, so I'm going to focus on some Node specifics I find essential.
私は新たなチームでNode.jsのプロジェクトを開始しようとしていました。これはNodeでの作業でいくつかのヒントをまとめる良い機会かもしれないと思ったのが始まりです。これは、通常の意味でのスタイルガイドではありません。あなたが既にJavaScriptのインデントおよび他のすべての宗教の立場で自分の意見を持っていると仮定し、私が本質的ないくつかのNodeの仕様をフォーカスするつもりです。

If you're looking to define a more complete style-guide, including where to put those pesky curly braces, I recommend reading some of the following:
厄介な中かっこを配置する場所を含む、より完全なスタイルガイドの定義を探しているなら、次のいくつかを読むことをお勧めします

1. Avoid this and new

1. thisとnewを避ける

This may seem like quibbling over coding style, but in Node.js it's an important way of keeping your functions easily composable and reusable.
これはコーディングスタイルの上言い逃れのように思えるかもしれませんが、Node.jsで、関数が簡単に作成でき、再利用するための重要な手段です。

Because Node.js involves passing around many callbacks and using a lot of higher-level functions to manage control flow; you should make sure functions are easy to move around without having to bind to a specific context.
Node.jsは多くのコールバックを渡すと、制御フローを管理する為、高レベル関数を使用するため、関数が特定のコンテキストにバインドすることなく、移動が簡単に確認できなければなりません。クロージャ オブジェクトのメソッドを好むし、クロージャで明示的な引数を好みます。

// contrived example using prototypes and 'this'
// prototypeの 'this'を使用した不自然な例

function DB(url) {
    this.url = url;
}

DB.prototype.info = function (callback) {
    http.get(this.url + '/info', callback);
};

async.parallel([
    function (cb) {
        new DB('http://foo').info(cb);
    },
    function (cb) {
        new DB('http://bar').info(cb);
    }
], ...);
// // similar example using closures
// クロージャを使った同様の例

function DB(url) {
    return { info: async.apply(http.get, url + '/info') };
}

async.parallel([
    DB('http://foo').info,
    DB('http://bar').info
], ...);

// making all arguments explicit
// すべての引数を明示的にすること

var dbInfo = function (url, callback) {
    http.get(url + '/info', callback);
};

async.map(['http://foo', 'http://bar'], dbInfo, ...);

The final style, with all arguments made explicit, allows us to easily pass in an arbitrary list of URLs and perform the same operation on them all.
Using a more functional style will make your life significantly easier when it comes to combining functionality in this way.
すべての引数が明示された最終的なスタイルは、簡単にURLの任意のリストを渡し、それらすべてに同じオペレーションを実行することができます。関数的なスタイルを使用すると、このように関数を組み合わせることになり、とても簡単になります。

Of course, there are cases where making all arguments explicit would be undesirable, and cases where using constructors and prototypes are more efficient.
I simply recommend you prefer the latter styles over the former and justify your use of these features when necessary.
もちろん、全ての引数を明示的に作ることが望ましくないケースもあり、その場合、コンストラクタとプロトタイプを使用すると、より効率的です。私は単に前者より後者のスタイルを好むし、必要に応じてこれらの機能の使用を正当化することをお勧めします。

2. More, smaller functions

2. 小さな関数を多くする

Before you even start looking into control flow libraries like async >, you can tame much of the "callback hell" by simply breaking up your functions into smaller components.
asyncのような制御フローライブラリ探しをはじめる前に、単に小さなコンポーネントに関数を分割することによって「コールバック地獄」の多くを飼いならすことができます。

// a deeply-nested function including four asynchronous operations
// 4つの非同期操作を含む深くネストされた関数

function convertJsonToCsv(filename, target, callback) {
    readFile(filename, function (err, content) {
        if (err) {
            return callback(err);
        }
        parseJson(content, function (err, data) {
            if (err) {
                return callback(err);
            }
            convertToCsv(data, function (err, csv) {
                if (err) {
                    return callback(err);
                }
                writeFile(target, csv, callback);
            });
        });
    });
}
// the same functionality broken into smaller parts
// 同じ機能をより小さなパーツに分割

function convertJsonToCsv(filename, target, callback) {
    readJsonFile(filename, function (err, data) {
        if (err) {
            return callback(err);
        }
        writeCsvFile(target, data, callback);
    });
}

function readJsonFile(filename, callback) {
    readFile(filename, function (err, content) {
        if (err) {
            return callback(err);
        }
        parseJson(content, callback);
    });
}

function writeCsvFile(target, data, callback) {
    convertToCsv(data, function (err, csv) {
        if (err) {
            return callback(err);
        }
        writeFile(target, csv, callback);
    });
}

I'd recommend you try and keep your functions down to two asynchronous operations at a time, as in the above example, or to one iteration over a list (using async.map or similar).
Having more, smaller functions also makes it easier to recombine them in new ways later on.
上記の例のように、一度に2つの非同期のオペレーションをする機能を抑える、またはリストを1度イテレーション(async.mapまたは類似の使用)することをお勧めします。より小さな関数を有することで後で容易に新しい方法で再結合することができます。

Of course, you can further clean up your code using an asynchronous control flow library.
But this is the first step in getting things readable again.
もちろん、さらに非同期制御フロー·ライブラリを使用してコードをクリーンアップすることができます。しかし、これは可読性をあげるための最初のステップです。

3. Consistently asynchronous APIs

3. 一貫した非同期API

In order to keep your code predictable, a function should be either always asynchronous, or always synchronous.
コードを予測可能にしておくために、関数は、常に非同期、または常に同期している必要があります。次の点を考慮します。

var CACHE = {};

function getRecord(id, callback) {
    if (CACHE[id]) {
        return CACHE[id];
    }
    http.get('http://foo/' + id, callback);
}

When someone attempts to use this function, it's very easy to miss out the cached case and have code that never completes:
誰かがこの関数を使用する場合、完了しないコードがあり、キャッシュされたケースを非常に簡単に見落とします。

function getMyRecord(user, callback) {
    getRecord('record-' + user.id, callback);
}

The first time you call getMyRecord it will complete successfully, the second time it will never call the callback (because getRecord returns immediately with the cached result). This might halt execution of the request.
getMyRecord を初めて呼び出す時は、正常に完了しますが、 2度目はコールバックが呼ばれれることはありません。(GetRecordは、キャッシュされた結果を直ちに返しているため)これは、リクエストの実行を停止することがあります。

Another concern with the getRecord function is execution order:
もう一つの懸案が getRecord 関数の実行順です。

function getMyRecord(user, callback) {
    var r = getRecord('record-' + user.id, function (err, record) {
        if (record) {
            record.got = true;
        }
        return callback(err, record);
    });
    if (r) {
        r.got = true;
    }
    return r;
}

Code like this is just too difficult to reason about. In order to implement it reliably you'd have to duplicate code inside the getRecord callback, and after it (in case it's synchronous). You then have to do the same thing any time you call the getMyRecord function too. This very quickly becomes messy and confusing.
このようなコードの理由は信じられない程難しいです。 getRecord コールバック中、それ以後(同期である場合)に重複したコードを確実に実装する必要があります。 getMyRecord 関数を呼び出すたびに同じことを行う必要があります。これは、非常に早く、乱雑になり、混乱を招くことになります。

The correct way to handle this is to use process.nextTick to make getRecord asynchronous even when returning a cached result:
これに対処する正しい方法は、 process.nextTick を使用し、キャッシュされた結果を返すときに getRecord を非同期にします。

var CACHE = {};

function getRecord(id, callback) {
    if (CACHE[id]) {
        return process.nextTick(function () {
            return callback(null, CACHE[id]);
        });
    }
    http.get('http://foo/' + id, callback);
}

Now we need only consider the asynchronous case and our code becomes much more predictable.
非同期の場合を考慮することで、コードは非常に予測しやすくなります。

4. Always check for errors in callbacks

4. 常にコールバックでエラーをチェックする

Missing an error check inside a callback is a common cause of confusion in debugging.
The problem is that the program execution will continue and the failure may manifest itself elsewhere in the code and give you a different error message.
You then have to trace it back to the original location.
コールバック内でエラーチェックが不足していることが、デバッグで混乱する一般的な原因です。問題は、プログラムは実行が継続され、障害がコードの他の場所で発生し、別のエラーメッセージを与えることです。その後、元の場所に戻ってそれをトレースする必要があります。

Failing to explicitly check for errors inside a callback should be considered a bug.
I highly recommend you build this step into your code review process because it can be so easy to miss.
明示的に、コールバック内のエラー チェックで失敗するバグを考慮しなければなりません。コードレビュープロセスにこのステップを構築することで、簡単に見落とさないようなるので、お勧めします。

// wrong!
// 誤っている!

function writeCsvFile(target, data, callback) {
    convertToCsv(data, function (err, csv) {
        writeFile(target, csv, callback);
    });
}
// right
// 正しい

function writeCsvFile(target, data, callback) {
    convertToCsv(data, function (err, csv) {
        if (err) {
            return callback(err);
        }
        writeFile(target, csv, callback);
    });
}

If you want to reduce the extra code, you can do a one-line if statement when checking for the error: if (err) return callback(err);. Whichever style you choose, I recommend you are consistent, because this piece of code should be easy to spot and easy to notice when it's missing.
余分なコードを削減したい場合は、if文1行でエラーかどうかをチェックすることができます。 if (err) return callback(err);. どちらのスタイルを選択しても、コードのこの部分は、それが不足していると容易に気付き、簡単に見つからなければならないので、一貫していることをお勧めします。

5. Return on callbacks

5. コールバックの戻り値

Usually, you call a callback as the last thing you do inside a function. You might consider it synonymous with return, only JavaScript does not halt function execution when it hits it. It can be easy to accidentally let execution continue after calling a callback, when you really expected it to end. In order to make this easy to spot, and make sure execution stops in the way you expect, I recommend returning the callback function call.
通常、関数内の最後にコールバックを呼び出します。 return と同じ意味で考えるかもしれませんがJavaScriptでは、callbackは関数の実行を停止しません。終了すると思っていても、誤ってコールバックを呼び出し、実行を継続させてしまうことは容易にあり得ます。実行の停止が期待どおりかを簡単に見つける方法として、コールバック関数の呼び出しをreturnすることをお勧めします。

// wrong!
// 誤っている!

function writeCsvFile(target, data, callback) {
    convertToCsv(data, function (err, csv) {
        if (err) {
            callback(err); // oops!no return
        }
        // この行が呼び出されると、theres an error
        writeFile(target, csv, callback);
    });
}
// right
// 正しい

function writeCsvFile(target, data, callback) {
    convertToCsv(data, function (err, csv) {
        if (err) {
            return callback(err); // 実行はここで停止します。
        }
        writeFile(target, csv, callback);
    });
}

6. Only throw in synchronous functions

6. 同期関数だけをスローする

Unfortunately, you can't use try-catch blocks around asynchronous code.
That means any exceptions you throw will not be caught and bubble up to the top.
This can kill your entire server process if you don't have an uncaughtException handler set up.
Even in cases where you do, the error probably no longer has any meaningful context and you can't appropriately respond to a http request, for example.
残念ながら、非同期コードに try/catch ブロックを使用することはできません。それは、投げた例外がキャッチされ、トップに浮上することがないことを意味します。uncaughtExceptionハンドラが設定されていない場合、全体のサーバプロセスをkillすることができます。この場合、エラーは、恐らく意味のある、コンテキストを有しておらず、例えば、httpリクエストに適切にレスポンスすることはできません。

Always, always, always pass errors back to the callback in asynchronous functions.
As long as you're following the "Always check for errors in callbacks" rule the errors will be handled in the appropriate place.
常に、常に、常に非同期コールバック関数にエラーを戻します。"エラー コールバックを常にチェック" のルールに従っている限り、エラーは適切な場所で処理されます。

// wrong!
// 誤っている

function getRecord(id, callback) {
    http.get('http://foo/' + id, function (err, doc) {
        if (err) {
            return callback(err);
        }
        if (doc.deleted) {
            // getRecord関数の呼び出しで捕まらないでしょう
            throw new Error('Record has been deleted');
        }
        return callback(null, doc);
    });
}
// right
// 正しい 

function getRecord(id, callback) {
    http.get('http://foo/' + id, function (err, doc) {
        if (err) {
            return callback(err);
        }
        if (doc.deleted) {
            return callback(new Error('Record has been deleted'));
        }
        return callback(null, doc);
    });
}

7. Catch errors in sync calls

7. 同期呼び出しでエラーをキャッチ

This is essentially the same as the previous rule. If you're calling a synchronous function that might throw an exception, inside your asynchronous code, then calling your asynchronous code might also result in an exception.
これは本質的には以前のルールと同じです。例外をスローする可能性があり、同期関数を呼び出しているのであれば、非同期コードの内部には,非同期コードを呼び出すと、例外が発生する場合があります。

For example:
例えば、次のように

// wrong!
// 誤っている

function readJson(filename, callback) {
    fs.readFile(filename, function (err, content) {
        if (err) {
            return callback(err);
        }
        // uh-oh! this line might throw an exception if the content
        // is not valid JSON
        // おっと!コンテンツがある場合は、この行は、例外がスローされる可能性が
        // 有効なJSONではない
        var data = JSON.parse(content.toString());

        return callback(null, data);
    });
}
// right
// 正しい

function readJson(filename, callback) {
    fs.readFile(filename, function (err, content) {
        if (err) {
            return callback(err);
        }
        try {
            var data = JSON.parse(content.toString());
        }
        catch (e) {
            return callback(e);
        }
        return callback(null, data);
    });
}

8. Stick to the callback convention

8. コールバック慣例に固執する

There is a vocal minority on the Node.js mailing list and elsewhere which promotes the use of other strategies for handling asynchronous code. Some of them even providing "blocking-style" APIs. While they are interesting, and you might happily use promises or fibers inside your team, please consider sticking to the Node-style callbacks; where the callback is the last argument to an async function and the first argument to the callback is an error (if one occurred).
非同期コードを取り扱うための他の戦略の使用を促進する少数派の声が、Node.jsメーリングリストとどこか他の所にいます。中には、 "ブロッキングスタイル"のAPIを提供もしてくれます。興味深いものですし、チーム内でpromiseやfiberを使用する可能性があると思いますが、Nodeのコールバックスタイルにこだわった上で検討してください。コールバックは、非同期関数の最後の引数であり、コールバックへの最初の引数はエラーになります。(発生した場合)

The vast majority of Node developers follow this style, there is consensus on this. If you expect Node developers outside of your team to use your modules then you'll have a much easier time sticking to convention.
大多数 の Node 開発者の大半は このスタイルとコンセンサスに従っています。チーム外部の Node 開発者 がモジュールを使用する場合、 慣例に固執しているくらいの方が楽になるでしょう。

Wrap up

結論

While most of this is probably common-sense, I hope some of you find these rules useful.
これのほとんどは、おそらく常識的ですが、私はこれらのルールが役に立つことを願っています。