最近のトピックをきっかけに、「コールバックから麺が作られていたため、私のスタートアップは離陸しませんでした」というスタイルの絶え間ない話を聞きました。つい最近、私は小さなプロジェクトを完成させました(リンクを提供しないので、誰がプロファイルを見る必要があるかを疑わないようにします)。 もちろん、悪名高い「麺」の問題に遭遇しました。 そして、あなたはそれを信じないでしょう、私はフレームワークやトリックなしで冷静にそれを解決しました。
そこで、データベースから書籍の数を非同期で選択し、データベースから必要な書籍のスタックを非同期で選択し、データベースから書籍のメタデータを非同期で選択し、これをすべて単一のデータセットに減らしてテンプレートをレンダリングするというタスクがあるとします。 それは通常どのように見えますか?
exports.processRequest = function (request, response) { db.query('SELECT COUNT(id) FROM books', function (res1) {
さらに2、3の中間ステップを実行すると、すべてが非常に悪くなります。
では、簡単な質問をしてみましょう。なぜこの麺を書いたのですか? ここでは、3つのネストされたクロージャーが本当に必要ですか?
いいえ、もちろんです。 3番目の匿名関数から、2番目と1番目のクロージャーにアクセスする必要はまったくありません。 少しのコードを書き直してください:
exports.processRequest = function (request, response) { var dataset = {}; getBookCount(); function getBookCount () { db.query('SELECT COUNT(id) FROM books', onBookCountReady); } function onBookCountReady (res) {
お願いします。 コードは完全に線形になり、重要なことに、より構造化されました。 プログラムフロー全体が目の前にあるため、コードの論理ブロックには個別の関数が装飾されています。 フレームワークやトリッキーな構文はありません。 落とし穴はありません。リクエストとレスポンスのペアを処理するコンテキストでデータセットが閉じられているため、リクエスト間で共有されているデータに誤ってアクセスすることはできません。
何かを並列化する必要がある場合、すべてが少し複雑になります。 このようなタスクはありませんでしたが、もしあれば(たとえば、メタデータのセットが2つある場合)、次のように解決します。
function getMetaData () { var parallelExecutor = new ParallelExecutor({ meta1: getMetaData1, meta2: getMetaData2 }); function getMetaData1 () { db.query('smthng', onMetaData1Ready); } function getMetaData2 () { db.query('smthng', onMetaData2Ready); } function onMetaData1Ready (res) {
意味は同じです-関数のセットに対して個別のクロージャーを作成し、通常は「ヌードル」に結合し、それらを順番にペイントします。
この形式では、非同期コールバックはコードを乱雑にするだけでなく、逆にコードを構造化して読みやすくするようです。