非同期APIず遅延オブゞェクトの詳现

最新のプログラミング蚀語では、非同期で実行されるコヌドブロックを䜿甚できたす。 非同期アプロヌチを䜿甚するこずで埗られる柔軟性ず合わせお、それを䜿甚するこずを敢えおした人は、コヌドの理解ず保守がより難しくなりたす。 ただし、プログラマヌが盎面する耇雑さは、原則ずしお、新しいアプロヌチの圢で実甚的な解決策を芋぀けるか、抜象化のレベルを䞊げたす。 非同期プログラミングの堎合、そのようなツヌルは、 遅延結果たたは遅延 英語の遅延 -遅延、遅延型のオブゞェクトです。

この蚘事では、非同期の結果、コヌルバック関数、遅延オブゞェクト、およびそれらの機胜を返すための基本的なアプロヌチに぀いお説明したす。 JavaScriptでサンプルが提䟛され、遅延サンプルオブゞェクトが解析されたす。 この蚘事は、非同期プログラミングを理解し始めるプログラマヌ、および非同期プログラミングに粟通しおいるが、遅延オブゞェクトを所有しおいないプログラマヌに圹立ちたす。

同期および非同期呌び出し


すべおの関数は、同期および非同期圢匏で蚘述できたす。 䜕らかの蚈算を行うcalc関数があるずしたす。

通垞の「同期」アプロヌチの堎合、蚈算結果は戻り倀を介しお送信されたす。぀たり、結果は関数が実行された盎埌に利甚可胜になり、別の蚈算で䜿甚できたす。

 var result = calc(); another_calc(result * 2); 

コヌドは厳密に連続しお実行され、1行で埗られた結果は次の行で䜿甚できたす。 これは、次のステヌトメントが前のステヌトメントから論理的に続く堎合の定理の蚌明を連想させたす。

非同期呌び出しの堎合、結果を適切に取埗できたせん。 calc関数を呌び出しお、蚈算を実行しおその結果を取埗する必芁があるこずのみを瀺したす。 この堎合、次の行は前の行が完了するのを埅たずに実行を開始したす。 それでも、どうにかしお結果を取埗する必芁があり、ここでコヌルバックが圹立ちたす。これは、蚈算結果の到着時にシステムによっお呌び出される関数です。 結果は匕数ずしおこの関数に枡されたす。

 calc(function (result) { another_calc(result * 2); }); no_cares_about_result(); 

䟋からわかるように、関数にはシグネチャcalc(callback)があり、 callbackは結果を最初のパラメヌタヌずしお受け取りたす。

calcは非同期に実行されるため、 no_cares_about_result関数no_cares_about_result結果にアクセスできず、䞀般的に蚀えば、コヌルバックよりも早く実行できたす特にJavaScriptに぀いお蚀えば-呌び出された関数が本圓に非同期であるが、キャッシュからデヌタを取埗しない堎合など呌び出し元のコヌドが実行された埌に垞に実行されるこずが保蚌されおいたす。぀たり、コヌルバックの前に残りのコヌドが垞に実行されたす。これに぀いおは以䞋で説明したす。

そのようなコヌドは、その「盎接的な」同期アナログず同じセマンティックの負荷で、すでに倚少理解が難しくなっおいるこずを認めなければなりたせん。 非同期アプロヌチを䜿甚する利点は䜕ですか たず、システムリ゜ヌスの合理的な䜿甚においお。 たずえば、 calcが時間がかかる蚈算である堎合、たたは倖郚リ゜ヌスを䜿甚する堎合、その䜿甚には䞀定の遅延が生じるため、同期アプロヌチでは、埌続のすべおのコヌドは結果を埅機するように匷制され、 calc実行されるたで実行されたせん。 非同期アプロヌチを䜿甚するず、コヌドのどの郚分が結果に䟝存し、どの郚分が結果に圱響されないかを明瀺的に瀺すこずができたす。 この䟋では、 no_cares_about_resultは結果を明瀺的に䜿甚しないため、結果を期埅する必芁はありたせん。 コヌルバック内のコヌドセクションは、結果を受け取った埌にのみ実行されたす。

䞀般的に、ほずんどのAPIは本質的に非同期ですが、同期リ゜ヌスを暡倣できたす。リモヌトリ゜ヌスぞのアクセス、デヌタベヌスク゚リ、ファむルAPIでも非同期です。 APIが同期を「装う」堎合、そのような「芋せかけ」の成功は、結果の遅延に関連付けられたす。遅延が小さいほど良いです。 ロヌカルマシンで動䜜する同じファむルAPIはわずかな遅延を瀺し、倚くの堎合同期ずしお実装されたす。 リモヌトリ゜ヌスの操䜜ずデヌタベヌスぞのアクセスは、たすたす非同期的に実装されたす。

階局化された課題


非同期呌び出しの難しさは、非同期呌び出しを行うだけでなく、その結果を受け取っお、それを䜿甚しお別の非同期呌び出しで䜿甚する必芁がある堎合に、より顕著になりたす。 明らかに、連続しお実行されるいく぀かのコヌド行ぞの同期アプロヌチは、ここでは適切ではありたせん。

 var result = calc_one(); result = calc_two(result * 2); result = calc_three(result + 42); // using result 

コヌドは次の圢匏を取りたす。

 calc_one(function (result) { calc_two(result * 2, function (result) { calc_three(result + 42, function (result) { // using result }); }); }); 

第䞀に、このコヌドは「マルチレベル」になりたしたが、アクションの点では同期に䌌おいたす。 第二に、 calc_two 、 calc_three関数のシグニチャヌでは、 入力パラメヌタヌずコヌルバックが混圚しおいたす。実際には、 結果が返される堎所、぀たり出力パラメヌタヌです。 第䞉に、各関数が倱敗し、結果が埗られない堎合がありたす。

このコヌドは、コヌルバック関数を個別に定矩し、名前で枡すこずで簡玠化できたすが、これはすべおの問題の解決策ではありたせん。 ここでは新しいレベルの抜象化が必芁です。぀たり、 非同期の結果を抜象化できたす。

非同期結果


そのような結果は䜕ですか 実際、これは結果がい぀か来るか、すでに到着しおいるずいう情報を含むオブゞェクトです。 結果ぞのサブスクラむブは同じコヌルバックを介しお実行されたすが、このオブゞェクトにカプセル化され、非同期関数が入力パラメヌタヌずしおコヌルバックを実装するこずを矩務付けなくなりたした。

実際、結果オブゞェクトには次の3぀のこずが必芁です結果をサブスクラむブする機胜、結果の到着を瀺す機胜これはAPIクラむアントではなく非同期関数自䜓によっお䜿甚されたすを実装し、この結果を保存する

そのようなオブゞェクトの重芁な特城は、その状態の特異性でもありたす。 このようなオブゞェクトには、1結果がない、2結果があるずいう2぀の状態がありたす。 さらに、遷移は、最初の状態から2番目の状態にのみ可胜です。 結果が埗られるず、それが存圚しない状態になったり、別の結果を持぀状態になったりするこずはできなくなりたす。

このオブゞェクトの次の簡単なむンタヌフェむスを考えおみたしょう。

 function Deferred () // constructor function on (callback) function resolve (result) 

onメ゜ッドはコヌルバックを受け入れたす。 コヌルバックは、結果が利甚可胜になるずすぐに呌び出され、パラメヌタヌずしお枡されたす。 これは、パラメヌタヌずしお枡される通垞のコヌルバックずの完党な類掚です。 コヌルバック登録の時点で、オブゞェクトは結果のある状態ず結果のない状態にある堎合がありたす。 ただ結果がない堎合、コヌルバックは到着時に呌び出されたす。 結果がすでに存圚する堎合、コヌルバックはすぐに呌び出されたす。 どちらの堎合も、コヌルバックは1回呌び出され、結果を取埗したす。

resolveメ゜ッドを䜿甚するず、オブゞェクトを結果のある状態に倉換リゟルバヌし、この結果を指定できたす。 このメ゜ッドはべき等です 。぀たり、 resolve呌び出しを繰り返しおもオブゞェクトは倉曎されたせん。 結果のある状態に遷移するず、登録されたすべおのコヌルバックが呌び出され、 resolve呌び出し埌に登録されるすべおのコヌルバックがすぐに呌び出されたす。 どちらの堎合 resolve呌び出しの前埌の登録でも、オブゞェクトがそれを保存するずいう事実のために、コヌルバックは結果を受け取りたす。

この動䜜を持぀オブゞェクトは、 遅延  promiseおよびfutureずも呌ばれたす ず呌ばれたす。 単玔なコヌルバックよりも、いく぀かの利点がありたす。

1.結果からの非同期関数の抜象化各非同期関数はコヌルバックパラメヌタヌを提䟛する必芁がなくなりたした。 結果のサブスクリプションはクラむアントコヌドに残りたす。 たずえば、結果が䞍芁な堎合は、結果をサブスクラむブする必芁はありたせんコヌルバックずしおnoop関数を枡すのず䌌おいたす。 非同期関数のむンタヌフェむスはよりクリヌンになりたす。重芁な入力パラメヌタヌのみを持ち、䞍特定数のパラメヌタヌ、オプションパラメヌタヌなどを持぀関数をより自信を持っお䜿甚するこずが可胜になりたす。
2.結果の状態からの抜象化コヌドのクラむアントは、結果の珟圚の状態を確認する必芁がなく、単にハンドラヌに眲名するだけで、結果が到着したかどうかを考えたせん。
3.耇数のサブスクリプションの可胜性耇数のハンドラヌに眲名するこずができ、結果を受信するずそれらすべおが呌び出されたす。 コヌルバックスキヌムでは、たずえば、関数のグルヌプを呌び出す関数を䜜成する必芁がありたす。
4.遅延オブゞェクトの「代数」など、いく぀かの远加のアメニティ。これにより、オブゞェクト間の関係を刀断したり、チェヌンで実行したり、そのようなオブゞェクトのグルヌプを正垞に完了したりできたす。

次の䟋を考えおみたしょう。 非同期関数getData(id, onSuccess)があるずしたす。これは、2぀のパラメヌタヌを受け取りたす受信する芁玠のidず、結果を取埗するためのコヌルバックです。 その䜿甚の兞型的なコヌドは次のようになりたす。

 getData(id, function (item) { // do some actions with item }); 

Deferredを䜿甚しおこれを曞き換えたす。 この関数にはシグネチャgetData(id)あり、次のように䜿甚されたす。

 getData(id).on(function (item) { // do some actions with item }); 

この堎合、コヌドはそれほど耇雑にならず、アプロヌチは単玔に倉曎されおいたす。 結果は、遅延ずしお関数の戻り倀に枡されたす。 ただし、埌で明らかになるように、より耇雑なケヌスでは、deferredを䜿甚するずコヌドの可読性が向䞊したす。

゚ラヌ凊理


このようなオブゞェクトを䜿甚する堎合の゚ラヌ凊理の問題は合理的です。 同期コヌドでは、䟋倖メカニズムが広く䜿甚されおいたす。これにより、゚ラヌが発生した堎合に「ロヌカル」コヌ​​ドを倧幅に耇雑化せずにすべおの゚ラヌをキャッチしお凊理できるため、プログラマがくしゃみごずにチェックを曞く必芁がなくなりたす。
非同期コヌドおよびコヌルバックのある回路で䟋倖を䜿甚するのは困難です。䟋倖は結果のように非同期に到着するため、 try非同期関数の呌び出しをフレヌミングしおもキャッチできないためです。 ゚ラヌを考慮するず、実際には、これは関数の別の結果負の結果ず蚀うこずもできたすであり、゚ラヌのオブゞェクト䟋倖は戻り倀ずしお機胜したす。

そのような結果は、成功ず同様に、コヌルバックずしお実装されたす errback 、 ゚ラヌからの単語の再生およびbackずも呌ばれたす 。

Deferredトレヌニングオブゞェクトを匷化しお、成功ず倱敗に別々のサブスクリプションを提䟛できるようにしたす。぀たり、 onメ゜ッドを再凊理しon resolveたす。

 function on (state, callback) 

最初のパラメヌタヌずしお、 E_SUCCESS 、 E_ERRORなどの2぀の倀を持぀列挙型の倀を枡すこずができたす。 読みやすくするために、䟋では単玔な文字列倀「success」、「error」を䜿甚したす。 たた、このメ゜ッドを匷化し、 Deferredオブゞェクト自䜓を返すようにしたす。 これにより、サブスクリプションチェヌンの䜿甚が蚱可されたすJavaScriptに非垞に固有の手法。

resolve方法はそれに応じお倉曎されたす。

 function resolve (state, result) 

最初のパラメヌタヌずしお、状態はDeferred ゚ラヌ、成功オブゞェクトに枡され、2番目は結果です。 状態ルヌルは、そのような倉曎されたオブゞェクトにも適甚されたす。結果のある状態ぞの遷移埌、オブゞェクトはその状態を別のものに倉曎できたせん。 これは、たずえば、オブゞェクトが成功状態になった堎合、゚ラヌに察しお登録されたすべおのハンドラヌが機胜しないこずを意味したす。

そのため、 getData関数getData䜕らかの゚ラヌデヌタなし、誀った入力、倱敗などで倱敗するようにしたす。
コヌドは次の圢匏を取りたす。

 getData(id) .on('success', function (item) { // do some actions with item }) .on('error', function (err_code) { // deal with error }); 

より珟実的な䟋を考えおみたしょう。぀たり、暙準のNode.jsから暙準のfs.readFileメ゜ッドを取埗したす。 このメ゜ッドは、ファむルの読み取りに䜿甚されたす。 蚘事の冒頭で、ほがすべおの関数を同期スタむルたたは非同期スタむルで蚘述できるこずが述べられたした。 暙準のNode.jsラむブラリでは、ファむルAPIは䞡方のスタむルで定矩され、各関数には独自の同期察応物がありたす。

たずえば、非同期バヌゞョンのreadFileを䜿甚し、Deferredを䜿甚するように調敎したす。

 function readFileDeferred (filename, options) { var result = new Deferred; fs.readFile(filename, options, function (err, data) { if (err) { result.resolve('error', err); } else { result.resolve('success', data); } }); return result; } 

このような関数は、成功ず゚ラヌの関数を別々に登録できるため、䜿甚するのが倚少䟿利です。

説明されおいる機胜は、ほずんどの堎合に十分ですが、延期にはさらに可胜性がありたす。これに぀いおは以䞋で説明したす。

高床な遅延オブゞェクト機胜


1.結果オプションの数に制限はありたせん。 この䟋では、 Deferredオブゞェクトを䜿甚しお、成功ず゚ラヌの2぀の結果が埗られたした。 他のカスタムオプションの䜿甚を劚げるものは䜕もありたせん。 幞いなこずに、状態ずしお文字列倀を䜿甚したため、列挙型を倉曎せずに結果のセットを定矩できたす。
2.結果のすべおのオプションをサブスクラむブする機胜。 これは、すべおの皮類の䞀般化されたハンドラヌに䜿甚できたすこれは、段萜1ず組み合わせお最も理にかなっおいたす。
3.サブオブゞェクトプロミスを䜜成したす。 Deferredオブゞェクトのむンタヌフェむスから、クラむアントコヌドがresolveメ゜ッドにアクセスできるこずがDeferredたすが、実際には、サブスクラむブする機胜のみが必芁です。 この改善の本質はpromiseメ゜ッドの導入ですpromiseメ゜ッドはDeferredオブゞェクトの「サブセット」を返し、そこからサブスクリプションのみを利甚できたすが、結果は蚭定したせん。
4.延期された状態から別の状態ぞの状態の転送。オプションで、結果を倉換したす。 これは、マルチレベルの呌び出しに非垞に圹立ちたす。
5.他の遅延のセットの結果に䟝存する、遅延の䜜成。 この改善の本質は、非同期操䜜のグルヌプの結果をサブスクラむブするこずです。
2぀のファむルを読み取り、䞡方で䜕か面癜いこずをする必芁があるずしたす。 これを行うには、 readFileDeferred関数を䜿甚したす。

 var r1 = readFileDeferred('./session.data'), r2 = readFileDeferred('./data/user.data'); var r3 = Deferred.all(r1, r2); r3.on('success', function (session, user) { session = JSON.parse(session); user = JSON.parse(user); console.log('All data recieved', session, user); }).on('error', function (err_code) { console.error('Error occured', err_code); }); 

Deferred.allは新しいDeferredオブゞェクトを䜜成し、枡されたすべおの匕数がこの状態に枡されるず成功状態に切り替わりたす。 そうするこずで、すべおの遅延の結果も匕数ずしお受け取りたす。 少なくずも1぀の匕数が゚ラヌ状態になるず、 Deferred.allの結果もこの状態になり、結果ずしお゚ラヌ状態になった匕数の結果を取埗したす。

JavaScriptの遅延機胜


JavaScriptにはマルチスレッドが存圚しないこずに泚意しおください。 コヌルバックがsetInterval / setTimeoutたたはむベントによっお蚭定された堎合、珟圚のコヌドの実行を䞭断したり、䞊行しお実行したりするこずはできたせん。 これは、非同期関数の結果が即座に到着した堎合でも、珟圚のコヌドの実行の完了埌にのみ受信されるこずを意味したす。

JavaScriptでは、関数は任意の数のパラメヌタヌず任意のコンテキストで呌び出すこずができたす。 これにより、コヌルバックに必芁な数のパラメヌタヌを転送できたす。 たずえば、非同期関数が倀のペア(X, Y)返す堎合、それらは2぀のフィヌルドを持぀オブゞェクト、たたは2぀の倀を持぀リストタプルの即興の類䌌物ずしお送信できたす。たたは、この目的でコヌルバックの最初の2぀の匕数を䜿甚できたす。

この堎合のコヌルバック呌び出しは、次の圢匏を取るこずができたす。
 callback.call(this, X, Y); 

JavaScriptはリンクを䜿甚し、メモリの解攟はガベヌゞコレクタヌによっお制埡されたす。 遅延オブゞェクトは、非同期関数の内郚結果の到着を通知するためず倖郚結果を取埗するための䞡方の䞡方で必芁です。メモリを操䜜するより厳栌なモデルを持぀蚀語では、そのようなオブゞェクトの有効期間の正しい凊理に泚意する必芁がありたす。

既存の据え眮き


1. jQueryには、 $.Deferredオブゞェクト ドキュメント がありたす。 成功、゚ラヌ、進行通知ぞのサブスクリプションもサポヌトされおいたす。結果が到着する前に生成される䞭間むベント。 状態を別のDeferred thenメ゜ッドに転送したり、Deferredリストの結果 $.when でDeferredを登録したり、 promiseを䜜成したりできたす。
すべおのラむブラリajaxメ゜ッドは、そのようなオブゞェクトの玄束を返したす。
2. qラむブラリは遅延オブゞェクトを実装したす。非同期関数のチェヌンを䜜成するこずが可胜で、遅延リストの結果によっお遅延を登録できたす。
3. async.jsラむブラリを䜿甚するず、非同期呌び出しでフィルタヌ/マップ/削枛を䜿甚し、非同期呌び出しのチェヌンずグルヌプを䜜成できたす。
4. when.jsラむブラリでは、deferredの䜿甚も蚱可されたす。
5. Dojo Toolkitには、Deferredオブゞェクト ドキュメント が含たれおいたす。
6. Pythonの兄匟蚀語では、むベント駆動型のTwistedフレヌムワヌクにDeferredオブゞェクト documentation がありたす。 この実装は非垞に叀く、結果の遅延ずいうアむデアの祖先の暩利を䞻匵する堎合がありたす。
成功、゚ラヌ、䞡方の結果のサブスクリプションをサポヌトしたす。 オブゞェクトを䞀時停止できたす。
7. Deferredに興味があるため、このオブゞェクトの独自のバヌゞョンを䜜成したした  ドキュメント 、 ゜ヌスコヌド 、 テスト 。 この蚘事で説明されおいる倚くの機胜がサポヌトされおいたす。

それだけです、ご枅聎ありがずうございたした。

Source: https://habr.com/ru/post/J175947/


All Articles