現在、多数の非同期操作が需要に関与しており、実際のページは単一ページのアプリケーションにますます傾いています。将来的には、バックボーン、角度、ノックアウト、その他のフレームワークを使用できますが、非同期プログラミングの問題は最初に直面する問題です。ノードの台頭により、非同期プログラミングは非常にホットなトピックになりました。学習と実践の期間の後、非同期プログラミングの詳細が要約されます。
1。非同期プログラミングの分類
非同期問題を解決する方法には、一般に次のものが含まれます。ダイレクトコールバック、PUB/サブモード(イベントモード)、非同期ライブラリコントロールライブラリ(Async、Whenなど)、Promise、Generatorなど。
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);
function load(){
...
}
if(img.complete){
負荷();
} それ以外 {
img.addeventlistener( 'load'、load);
}
img.addeventlistener( 'error'、function(){
//何かがうまくいかなかった
......
});
b。例外をうまく処理できません
結論:イベントメカニズムは、同じオブジェクトで繰り返されるイベントに対処するのに最適であり、コールバック関数がバインドされる前にイベントを考慮する必要はありません。
1.3非同期制御ライブラリ
現在の非同期ライブラリには、主にq、when.js、win.js、rsvp.jsなどが含まれます。
これらのライブラリの特徴は、コードが線形であり、自然の習慣に沿って上から下まで書くことができることです。
悪いことはスタイルも異なっており、学習コストを読んで増やすのは不便です。
1.4約束
約束は約束として中国語に翻訳されます。個人的には、非同期に完了した後、外部の結果(成功または失敗)を与え、結果がもはや変わらないことを約束します。言い換えれば、約束は操作の最終的な返品結果値を反映しています(約束は、操作の単一の完了から返された最終的な値を表します)。現在、ES6仕様に約束が導入されており、ChromeやFirefoxなどの高度なブラウザがこのネイティブ方法を内部的に実装しています。
以下は、次の側面からの約束の特徴です。
1.4.1ステータス
保留、充足、拒否の3つの状態が含まれています。 3つの州は、2つの移行を受けることができます(保留中--->充足、保留中 - >拒否された)。州の移行は一度しか発生しません。
1.4.2次にメソッド
次に、非同期イベントが完了した後、コールバック関数を指定するために使用されます。
この方法は、魔法に満ちた約束をしている魂の約束の方法であると言えます。次のようにいくつかの特定の症状があります:
a)その後、メソッドは約束を返します。これにより、複数の非同期操作のシリアル操作が可能になります。
上記の図の黄色の円1に関して、値の処理は約束のより複雑な部分です。値の処理は、オブジェクトを約束する2つの状況と非普及オブジェクトに分けられます。
値が約束の種類ではない場合は、2番目の約束の解決のパラメーター値として値を使用してください。 Promiseタイプの場合、Promise2のステータスとパラメーターは値によって完全に決定されます。 Promsie2は価値の人形であり、Promise2は異なる非同期をつなぐブリッジにすぎないと考えることができます。
コードコピーは次のとおりです。
promise.prototype.then = function(onfulfill、onreected){
新しいPromise(function(Resolve、拒否){//ここでの約束はPromise2としてマークされています2
ハンドル({
onfulfiled:onfulfiled、
refereded:refereded、
解決:解決、
拒否:拒否
})
});
}
関数ハンドル(延期){
var handlefn;
if(state === 'fulfill'){
handlefn = deferred.onfulfilled;
} else if(state === '拒否'){
handlefn = deferred.onejected;
}
var ret = handlefn(value);
deferred.resolve(ret); //現時点での解決は約束の解決であることに注意してください2
}
関数Resolve(val){
if(val && typeof val.then === 'function'){
Val.Then(Resolve); // valがPromise ObjectまたはClass Promiseオブジェクトである場合、Promise2の状態はvalによって完全に決定されます
戻る;
}
if(callback){//コールバックは指定されたコールバック関数です
コールバック(val);
}
}
b)複数の異なる非同期ライブラリ間の変換が実装されています。
非同期でその後に呼ばれるオブジェクトがあります。これは、Thenメソッドのオブジェクトを指します。オブジェクトオブジェクトに当時のメソッドがある限り、たとえば次のように変換できます。
コードコピーは次のとおりです。
var Deferred = $( 'aa.ajax'); // !! deferred.then === true
var p = promise.resolve(延期);
p.then(......)
1.4.3 CommonJS Promise/A仕様
現在、約束に関するPromise/AとPromise/A+仕様があります。これは、約束の実施が非常に複雑であることを示しています。
コードコピーは次のとおりです。
次に(fulfilledhandler、rexjededler、progresshandler)
1.4.4ノート
約束のコールバック関数が値を共有します。結果処理では、値は対応するコールバック関数のパラメーターとして渡されます。値がオブジェクトの場合は、値を簡単に変更しないように注意してください。
コードコピーは次のとおりです。
var p = promise.resolve({x:1});
p.then(function(val){
console.log( 'first callback:'+val.x ++);
});
p.then(function(val){
console.log( 'セカンドコールバック:' + val.x)
})
//最初のコールバック:1
// 2番目のコールバック:2
1.5ジェネレーター
上記のすべての方法は、非同期操作を完了するコールバック関数に基づいており、コールバック関数のカプセル化にすぎません。 ES6は、非同期操作を解決する方法を追加するジェネレーターを提案し、コールバック関数に基づいて完了しなくなりました。
ジェネレーターの最大の特徴は、機能を一時停止して再起動できることです。これは、非同期操作を解決するのに非常に役立ちます。ジェネレーターの一時停止とPromiseの例外処理を組み合わせることで、非同期プログラミングの問題をよりエレガントに解決できます。特定の実装リファレンス:カイルシンプソン
2。非同期プログラミングの問題
2.1例外処理
a)非同期イベントには、非同期リクエストの発行と処理結果の2つのリンクが含まれます。これらの2つのリンクは、イベントループを介して接続されています。次に、例外キャプチャを実行するためにキャッチを試してみると、キャプチャする必要があります。
コードコピーは次のとおりです。
試す {
asyncevent(コールバック);
} catch(err){
......
}
上記のコードは、コールバックで例外をキャッチすることができず、リクエストプロセスでのみ例外を取得できます。これには問題があります。リクエストの発行と要求処理が2人によって完了した場合、例外を処理するときに問題がありますか?
b)約束は例外配信を実装します。これは、実際のプロジェクトでコードがブロックされないようにするためのいくつかの利点をもたらします。ただし、多くの非同期イベントがある場合、どの非同期イベントが例外を生成するかを見つけることは容易ではありません。
コードコピーは次のとおりです。
//シーンの説明:競争力のある情報を含むCRMに価格アラーム情報を表示します。ただし、競争力のある情報を取得するには長い時間がかかります。遅いクエリを避けるために、バックエンドはレコードを2つのピースに分割して個別に取得します。
//ステップ1:競争情報に加えて、価格アラーム情報を取得します
関数getpricealarmdata(){
新しい約束を返す(function(resolve){
Y.io(url、{
方法:「取得」、
データ:パラメーション、
on:function(){
成功:function(id、data){
Resolve(alarmdata);
}
}
});
});
}
//アラーム情報を取得した後、競争情報を取得してください
getpricealarmdata()。then(function(data){
//競争力のある情報に加えて、データレンダリング
render(data);
新しい約束を返す(function(resolve){
Y.io(url、{
方法:「取得」、
データ:{alarmlist:data}、
on:function(){
成功:function(id、compdata){
Resolve(compdata);
}
}
});
});
})//すべてのデータを取得した後、競争情報のレンダリング
.then(function(data){
//入札情報をレンダリングします
レンダリング(データ)
}、function(err){
//例外処理
console.log(err);
});
上記のコードは、次のように変換できます。
コードコピーは次のとおりです。
試す{
//競争以外のアラーム情報を取得します
var alarmdata = alarmdataexceptcompare();
render(alarmdata);
//アラーム情報に基づく競争情報の問い合わせ
var compatedata = getCompareInfo(alarmdata);
レンダリング(比較);
} catch(err){
console.log(err.message);
}
上記の例では、例外は最後に処理されるため、特定のリンクで例外が発生した場合、どのイベントが生成されるかを正確に知ることができません。
2.2 jQuery。延期された問題
非同期操作もjQueryで実装されていますが、実装のPromise/A+仕様に準拠しておらず、主に以下の側面に反映されています。
a。パラメーターの数:標準の約束は1つのパラメーターのみを受け入れることができますが、jQueryは複数のパラメーターを渡すことができます
コードコピーは次のとおりです。
関数asyncinjquery(){
var d = new $ .deferred();
setimeout(function(){
D.Resolve(1、2);
}、100);
d.promise()を返します
}
asyncinjquery()。then(function(val1、val2){
console.log( 'output:'、val1、val2);
});
//出力:1 2
b。結果処理における例外の処理
コードコピーは次のとおりです。
関数asyncinpromise(){
新しい約束を返す(function(resolve){
setimeout(function(){
var jsonstr = '{"name": "mt}';
Resolve(jsonstr);
}、100);
});
}
asyncinpromise()。then(function(val){
var d = json.parse(val);
console.log(d.name);
})。それから(null、function(err){
console.log( 'show error:' + err.message);
});
//エラーを表示:入力の予期しない終了
関数asyncinjquery(){
var d = new $ .deferred();
setimeout(function(){
var jsonstr = '{"name": "mt}';
D.Resolve(JSonstr);
}、100);
d.promise()を返します
}
asyncinjquery()。then(function(val){
var d = json.parse(val);
console.log(d.name);
})。それから(function(v){
console.log( 'success:'、v.name);
}、function(err){
console.log( 'show error:' + err.message);
});
// Uncaught Syntaxerror:予期しない入力の終わり
これからは、コールバック関数の実行中に例外をキャプチャできるコールバック関数の結果を処理することが約束されていることがわかりますが、jquery.deferredはできません。