ホームページ >ウェブフロントエンド >jsチュートリアル >Nodejs 非同期プログラミングの詳細な説明_node.js

Nodejs 非同期プログラミングの詳細な説明_node.js

WBOY
WBOYオリジナル
2016-05-16 16:29:191485ブラウズ

現在の要件には多数の非同期操作が含まれており、実際のページはますます単一ページ アプリケーションに傾いています。将来的には、backbone、angular、knockout などのフレームワークを使用する可能性がありますが、最初に直面する必要があるのは非同期プログラミングに関する問題です。ノードの台頭により、非同期プログラミングが非常にホットなトピックになっています。一定期間の学習と実践の後、非同期プログラミングの詳細がいくつかまとめられます。

1. 非同期プログラミングの分類

非同期の問題を解決する方法には、一般に、ダイレクト コールバック、パブリッシュ/サブスクライブ モード (イベント モード)、非同期ライブラリ制御ライブラリ (async、when など)、Promise、ジェネレーターなどが含まれます。
1.1 コールバック関数

コールバック関数は、非同期の問題を解決するためによく使用されるメソッドであり、よく参照され、理解しやすく、ライブラリや関数に実装するのが非常に簡単です。これは、非同期プログラミングを使用するときに誰もがよく使用する方法でもあります。

しかし、コールバック関数メソッドには次の問題があります:

1. 邪悪な入れ子ピラミッドを形成する可能性があり、コードが読みにくくなります。

2. 対応できるコールバック関数は 1 つだけですが、これは多くのシナリオで制限となります。

1.2 パブ/サブモード (イベント)

このモードはイベント モードとも呼ばれ、コールバック関数のイベント化であり、jQuery などのライブラリで非常に一般的です。

イベント発行サブスクライバー モデル自体には同期呼び出しと非同期呼び出しの問題はありませんが、ノードでは、エミット呼び出しはイベント ループと非同期でトリガーされることがほとんどです。このモードは、ビジネス ロジックを分離するためによく使用されます。イベント発行者は、登録されたコールバック関数を意識する必要がなく、メッセージを通じてデータを柔軟に転送できます。

このモードの利点は次のとおりです: 1. 理解しやすい; 2. 1 つのコールバック関数に限定されなくなりました。

欠点: 1. クラス ライブラリを使用する必要がある; 2. イベントとコールバック関数の順序が非常に重要

コードをコピーします コードは次のとおりです:
var img = document.querySelect(#id);
img.addEventListener('load', function() {
// 画像の読み込みが完了しました

});
img.addEventListener('error', function() {
// 問題が発生しました
……
});

上記のコードには 2 つの問題があります:

a. img は実際にはロードされており、ロード コールバック関数はこの時点でのみバインドされているため、コールバックは実行されませんが、対応するコールバック関数が実行されることを期待します。

コードをコピーします コードは次のとおりです:
var img = document.querySelect(#id);
関数load() {
...
}
if(img.complete) {
ロード();
} else {
img.addEventListener('load',load);
}
img.addEventListener('error', function() {
// 問題が発生しました
……
});

b. 例外をうまく処理できない

結論: イベント メカニズムは、同じオブジェクトで繰り返し発生する処理に最適です。コールバック関数がバインドされる前にイベントの発生を考慮する必要はありません。

1.3 非同期制御ライブラリ

現在の非同期ライブラリには主に Q、when.js、win.js、RSVP.js などが含まれます。

これらのライブラリの特徴は、コードが直線的であり、自然な習慣に沿って上から下に記述できることです。

欠点は、スタイルが異なるため読みにくくなり、学習コストが増加することです。

1.4 約束

Promise は中国語に訳すと約束となりますが、私の個人的な理解では、非同期完了後に外部の結果 (成功または失敗) を与え、その結果が変わらないことを約束します。言い換えれば、Promise は操作の最終的な戻り値を反映します (Promise は、操作の 1 回の完了から返される最終的な値を表します)。現在、Promise は ES6 仕様に導入されており、Chrome や Firefox などの先進的なブラウザはこのネイティブ メソッドを内部的に実装しているため、非常に使いやすくなっています。

Promise の特徴を次の側面から分析してみましょう:

1.4.1 ステータス

保留、履行、および拒否の 3 つの状態が含まれます。3 つの状態の間で発生できる遷移は 2 つだけ (保留中 ---> 履行、保留中 --> 拒否)、状態遷移は 1 回だけ発生します。

1.4.2 メソッド

then メソッドは、非同期イベントの完了後のコールバック関数を指定するために使用されます。

このメソッドは、Promise を魔法に満ちたものにする、Promise の魂のメソッドと言えます。以下に示すような具体的な症状がいくつかあります:

a) then メソッドは Promise を返します。これにより、複数の非同期操作のシリアル操作が可能になります。

上の図の黄色の丸 1 の値の処理については、Promise のより複雑な部分であり、値の処理は Promise オブジェクトと非 Promise オブジェクトの 2 つの状況に分かれています。

value が Promise 型でない場合は、2 番目の Promise のリゾルブのパラメータ値として value を使用するだけで、Promise 型の場合は、promise2 のステータスとパラメータは完全に value によって決定されると考えることができます。 promsie2 は完全に value の操り人形であり、promise2 は異なる非同期のものを接続する単なるブリッジです。

コードをコピー コードは次のとおりです:

Promise.prototype.then = function(onFulfilled, onRejected) {
    return new Promise(function(resolve, respect) { //此处的Promise标注はpromise2
        ハンドル({
            onFulfilled: onFulfilled、
            onRejected: onRejected,
            解決: 解決、
            拒否: 拒否
        })
    });
}
関数ハンドル(遅延) {
    var handleFn;
    if(状態 === '満たされた') {
        handleFn = deferred.onFulfilled;
    } else if(state === '拒否') {
        handleFn = deferred.onRejected;
    }
    var ret = handleFn(値);
    deferred.resolve(ret);                           //注意、今回の解決策はpromise2の解決策です
}
function resolve(val) {
    if(val && typeof val.then === '関数') {
        val.then(解決);                           // val が promise オブジェクトまたは promise オブジェクトの場合、promise2 の状態は val によって完全に決定されます
        戻る;
    }
    if(callback) { // コールバックは指定された回调関数
        コールバック(val);
    }
}

b) 複数の異なるブロック間の変換が実行されます。

异步中に呼び出し可能なオブジェクトが存在します。これは、then メソッドを持つオブジェクトを意味します。ただ 1 つのオブジェクトが then メソッドを持ち、それに対して実行できるオブジェクトを指します。例:

复制代 代码如下:

var deferred = $('aa.ajax');      // !!deferred.then === true
var P = Promise.resolve(deferred);
p.then(……)

1.4.3 commonJS Promise/A规范

現在、Promise の規定に関しては Promise/A および Promise/A の規定が存在しており、これは Promise の実現が複雑であることを示しています。

复制代 代码如下:
then(fulfilledHandler、rejectedHandler、progressHandler)

1.4.4 注意事项

Promise の 1 つの戻り関数は値を共有しており、結果処理で値がパラメータとして渡される対応する戻り関数です。値がオブジェクトの場合、簡単に変更できる値は無視されます。

var p = Promise.resolve({x: 1});
p.then(function(val) {
    console.log('最初のコールバック: ' val.x );
});
p.then(function(val) {
    console.log('2 番目のコールバック: ' val.x)
})
// 最初のコールバック: 1
// 2 番目のコールバック: 2

1.5 ジェネレーター

上記のメソッドはすべて、非同期操作を完了するためのコールバック関数に基づいています。これらはコールバック関数のカプセル化にすぎません。ジェネレーターは ES6 で提案されており、非同期操作を解決する方法が追加され、コールバック関数に依存しなくなります。

Generator の最大の特徴は、関数を一時停止および再開できることです。この機能は、非同期操作を解決するのに非常に役立ちます。 Generator の一時停止と Promise の例外処理を組み合わせると、非同期プログラミングの問題をよりエレガントに解決できます。特定の実装リファレンス: Kyle Simpson

2. 非同期プログラミングの問題

2.1 例外処理

a) 非同期イベントには、非同期リクエストの発行と結果処理の 2 つのリンクが含まれます。これらの 2 つのリンクは、イベント ループを通じて接続されます。 Try catch を使用して例外をキャプチャする場合は、例外を個別にキャプチャする必要があります。

コードをコピーします コードは次のとおりです:

{
を試してください asyncEvent(コールバック); } キャッチ(エラー) {

}

上記のコードはコールバックで例外をキャプチャできませんが、リクエスト プロセスでのみ例外を取得できます。これにより問題が発生します。リクエストの発行とリクエストの処理が 2 人で完了した場合、例外を処理するときに問題が発生しますか?

b) Promise は例外配信を実装します。これによりいくつかの利点がもたらされ、コードが実際のプロジェクトでブロックされないことが保証されます。ただし、非同期イベントが多数ある場合、どの非同期イベントが例外を引き起こしたかを見つけるのは簡単ではありません。

コードをコピーします コードは次のとおりです:
// シナリオの説明: CRM に競合情報を含む価格アラーム情報を表示します。ただし、競合情報の取得には時間がかかります。クエリの遅延を避けるために、バックエンドはレコードを 2 つの部分に分割し、別々に取得します。
// ステップ 1: 競合情報に加えて、価格アラーム情報を取得します
function getPriceAlarmData() {
新しい Promise(function(resolve) を返す {
Y.io(url, {
メソッド: 'get',
データ: params,
on: function() {
成功: function(id, data) {
解決(アラームデータ);
}
}
});
});
}
// アラーム情報取得後、大会情報を取得
getPriceAlarmData().then(function(data) {
//データレンダリング(競技情報を除く)
レンダリング(データ);
新しい Promise(function(resolve) を返す {
Y.io(url, {
メソッド: 'get',
データ: {alarmList: data},
on: function() {
成功: function(id, compData) {
resolve(compData);
}
}
});
});
}) // すべてのデータを取得したら、競合情報をレンダリングします
.then(関数(データ) {
// コンテスト情報をレンダリングします
レンダリング(データ)
}、関数(エラー) {
//例外処理
console.log(err);
});

上記のコードは次のように変換できます:

コードをコピーします コードは次のとおりです:

試してみてください{
// 競技以外のアラーム情報を取得
var アラームデータ = アラームデータ例外比較();
render(alarmData);
// アラーム情報に基づいて競合情報を問い合わせます
var CompareData = getCompareInfo(alarmData);
render(compareData);
} キャッチ(エラー) {
console.log(err.message);
}

上記の例では、例外処理が最後に配置されているため、特定のリンクで例外が発生した場合、どのイベントが原因であるかを正確に知ることができません。

2.2 jQuery.Deferred の問題

非同期操作は jQuery にも実装されていますが、この実装は主に次の点で Promise/A 仕様に準拠していません。

a. パラメータの数: 標準 Promise は 1 つのパラメータのみを受け入れることができますが、jQuery は複数のパラメータを渡すことができます

コードをコピーします コードは次のとおりです:

関数 asyncInJQuery() {
var d = new $.Deferred();
setTimeout(function() {
d.resolve(1, 2);
}, 100);
d.promise()
を返します }
asyncInJQuery().then(function(val1, val2) {
console.log('出力: ', val1, val2);
});
// 出力: 1 2

b. 結果処理での例外の処理

コードをコピーします コードは次のとおりです:

関数 asyncInPromise() {
新しい Promise(function(resolve) を返す {
setTimeout(function() {
var jsonStr = '{"name": "mt}';
solve(jsonStr);
}, 100);
});
}
asyncInPromise().then(function(val) {
var d = JSON.parse(val);
console.log(d.name);
}).then(null, function(err) {
console.log('エラーを表示: ' err.message);
});
// エラーを表示: 入力の予期しない終了
関数 asyncInJQuery() {
var d = new $.Deferred();
setTimeout(function() {
var jsonStr = '{"name": "mt}';
d.resolve(jsonStr);
}, 100);
d.promise()
を返します }
asyncInJQuery().then(function(val) {
var d = JSON.parse(val);
console.log(d.name);
}).then(function(v) {
console.log('success: ', v.name);
}、関数(エラー){
console.log('エラーを表示: ' err.message);
});
//キャッチされない SyntaxError: 入力の予期しない終了

このことから、Promise はコールバック関数の結果処理を実行し、コールバック関数の実行中に例外をキャプチャできるが、jQuery.Deferred はできないことがわかります。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。