成功するWebアプリケーションを作成するための鍵の1つは、ページごとに数十のAJAX呼び出しを行えることです。
これは典型的な非同期プログラミングの課題であり、非同期呼び出しの処理方法を選択すると、大部分はアプリが作成または中断され、ひいてはスタートアップ全体が失敗する可能性があります。
JavaScriptで非同期タスクを同期することは、非常に長い間深刻な問題でした。
この課題はバックエンドに影響を与えています Node.jsを使用する開発者 JavaScriptフレームワークを使用するフロントエンド開発者と同じくらい。非同期プログラミングは私たちの日常業務の一部ですが、多くの場合、課題は軽視され、適切なタイミングで検討されていません。
最初の最も簡単な解決策は、次の形式で提供されました。 コールバックとしての入れ子関数 。この解決策は、 コールバック地獄 、そしてあまりにも多くのアプリケーションがまだそれのやけどを感じています。
次に、 約束 。このパターンにより、コードがはるかに読みやすくなりましたが、Do n’t Repeat Yourself(DRY)の原則とはかけ離れていました。アプリケーションのフローを適切に管理するために、同じコードを繰り返さなければならないケースはまだ多すぎました。 async / awaitステートメントの形式での最新の追加により、JavaScriptの非同期コードは、他のコードと同じように読み取りと書き込みが簡単になりました。
これらの各ソリューションの例を見て、JavaScriptでの非同期プログラミングの進化について考えてみましょう。
これを行うために、次の手順を実行する簡単なタスクを調べます。
イラストアートを学ぶ方法
これらの呼び出しを同期するための古代の解決策は、ネストされたコールバックを介したものでした。これは、単純な非同期JavaScriptタスクには適切なアプローチでしたが、次のような問題のために拡張できませんでした。 コールバック地獄 。
3つの単純なタスクのコードは次のようになります。
const verifyUser = function(username, password, callback){ dataBase.verifyUser(username, password, (error, userInfo) => { if (error) { callback(error) }else{ dataBase.getRoles(username, (error, roles) => { if (error){ callback(error) }else { dataBase.logAccess(username, (error) => { if (error){ callback(error); }else{ callback(null, userInfo, roles); } }) } }) } }) };
各関数は、前のアクションの応答であるパラメーターで呼び出される別の関数である引数を取得します。
上記の文章を読むだけで、脳の凍結を経験する人が多すぎます。何百もの同様のコードブロックを持つアプリケーションがあると、たとえ自分でコードを書いたとしても、コードを保守する人にとってさらに問題が発生します。
database.getRoles
に気付くと、この例はさらに複雑になります。ネストされたコールバックを持つ別の関数です。
const getRoles = function (username, callback){ database.connect((connection) => { connection.query('get roles sql', (result) => { callback(null, result); }) }); };
この場合、保守が難しいコードがあることに加えて、DRYの原則にはまったく価値がありません。たとえば、エラー処理は各関数で繰り返され、メインのコールバックはネストされた各関数から呼び出されます。
非同期呼び出しのループなど、より複雑な非同期JavaScript操作は、さらに大きな課題です。実際、コールバックを使用してこれを行う簡単な方法はありません。これが、JavaScriptPromiseライブラリが 青い鳥 そして Q 非常に多くの牽引力を得ました。これらは、言語自体がまだ提供していない非同期リクエストに対して一般的な操作を実行する方法を提供します。
そこで、ネイティブJavaScriptPromisesが登場します。
約束 コールバック地獄を脱出するための次の論理的なステップでした。このメソッドはコールバックの使用を削除しませんでしたが、関数のチェーンを簡単にし、 コードを簡略化 、読みやすくします。
Promisesを配置すると、非同期JavaScriptの例のコードは次のようになります。
const verifyUser = function(username, password) { database.verifyUser(username, password) .then(userInfo => dataBase.getRoles(userInfo)) .then(rolesInfo => dataBase.logAccess(rolesInfo)) .then(finalResult => { //do whatever the 'callback' would do }) .catch((err) => { //do whatever the error handler needs }); };
この種の単純さを実現するには、例で使用されているすべての関数が 約束 。 getRoles
がどのようになっているのか見てみましょうメソッドが更新され、Promise
が返されます。
const getRoles = function (username){ return new Promise((resolve, reject) => { database.connect((connection) => { connection.query('get roles sql', (result) => { resolve(result); }) }); }); };
2つのコールバックとPromise
を含むPromise
を返すようにメソッドを変更しました。それ自体がメソッドからアクションを実行します。さて、resolve
およびreject
コールバックはPromise.then
にマップされますおよびPromise.catch
それぞれメソッド。
getRoles
に気付くかもしれません方法はまだ内部的に破滅現象のピラミッドになりがちです。これは、データベースメソッドがPromise
を返さないために作成される方法によるものです。データベースアクセスメソッドもPromise
を返した場合getRoles
メソッドは次のようになります。
const getRoles = new function (userInfo) { return new Promise((resolve, reject) => { database.connect() .then((connection) => connection.query('get roles sql')) .then((result) => resolve(result)) .catch(reject) }); };
運命のピラミッドは、Promisesの導入によって大幅に緩和されました。ただし、.then
に渡されるコールバックに依存する必要がありました。および.catch
Promise
のメソッド。
Promiseは、JavaScriptの最もクールな改善の1つへの道を開きました。 ECMAScript 2017 JavaScriptのPromiseの上にasync
の形式でシンタックスシュガーを導入しましたおよびawait
ステートメント。
このコードサンプルが示すように、メインスレッドをブロックすることなく、同期しているかのようにPromise
ベースのコードを記述できます。
const verifyUser = async function(username, password){ try { const userInfo = await dataBase.verifyUser(username, password); const rolesInfo = await dataBase.getRoles(userInfo); const logStatus = await dataBase.logAccess(userInfo); return userInfo; }catch (e){ //handle errors as needed } };
待っていますPromise
解決はasync
内でのみ許可されますverifyUser
を意味する関数async function
を使用して定義する必要がありました。
ただし、この小さな変更が行われると、await
が可能になります。任意のPromise
他の方法に追加の変更を加えることなく。
非同期関数は、JavaScriptの非同期プログラミングの進化における次の論理的なステップです。これらにより、コードがはるかにクリーンになり、保守が容易になります。関数をasync
として宣言する常にPromise
を返すようにしますだからもう心配する必要はありません。
JavaScriptの使用を開始する必要がある理由async
今日機能しますか?
try
/ catch
に依存しています。他の同期コードと同じように。.then
内にブレークポイントを設定するブロックは次の.then
に移動しません同期コードをステップスルーするだけだからです。ただし、ステップスルーすることはできますawait
同期呼び出しであるかのように呼び出します。Async / awaitステートメントは、JavaScriptPromisesの上に作成されたシンタックスシュガーです。これにより、Promiseベースのコードを同期しているかのように記述できますが、メインスレッドをブロックすることはありません。
JavaScriptでは、コールバック地獄は、非同期コードの不十分な構造化の結果として発生するコードのアンチパターンです。これは通常、プログラマーが非同期コールバックベースのJavaScriptコードで視覚的なトップダウン構造を強制しようとしたときに見られます。
JavaScriptのpromiseは、最終的に成功した結果値または失敗の理由に最終的に解決されることが期待されるプレースホルダー値のようなものです。