非同期の問題を解決しますか?

この記事では、完全に非同期の計算モデルを導入することにより、javascriptの非同期機能に関する問題の解決策について説明します。 概念自体について説明し、実装への参照を示します。 猫の下で興味を持ってください。


はじめに


主なものから始めましょう-同期的または非同期的に? 同期計算プロセスと不要な非同期機能がある場合、これは問題です。 さらに、非同期がより有利であり、一般的にベストプラクティスである場合、この問題を解決する必要があります。


ソリューションにはすでに何がありますか? コールバックがあり、約束があります。 ただし、promiseは修正されたコールバックであり、問​​題を単純化するだけで、解決はしません。 問題の実際の解決策として、すべてを1つのモデル(完全に同期または完全に非同期)に減らす必要があります。


最新の標準では、独自の約束が現れてから、非同期/待機が発生しました。これにより、計算プロセスを完全に同期したモデルに減らすことができます。 そして、問題は解決されたように見えますが、この解決策について多くの苦情があります。



あなたが批判する-申し出


async / awaitを忘れ、promiseを忘れましょう、どのように見えるべきでしょうか? 最小限の追加コードで、便利で一貫性がありますか? 関数のようなもの、非同期のみ:



つまり、同期JS関数を非同期に変更するとよいでしょう。 このタスクを完了するためのステップのリストを作成しましょう。



荒野へ!


それをすべて描写してみましょう。 条件により、非同期関数の定義は同期関数の定義とまったく同じでなければなりません。


 function A () { } 

最後から始める


callみましょう:



back



それでは、コールツリーを紹介しましょう


ここでは、最初の2つのポイントが実行されます。



 function A ( top ) { } 

同期復帰はもはや私たちにとって友達ではありません


ポイント3は、既に上記で説明したように、(コマンドによって)呼び出されると、特別なbackラッピングメソッドが1つのノードを呼び出しツリーに戻ります。 したがって、非同期リターンが実行されます。 すぐに、同期戻り(戻り)が考慮されなくなり、非同期呼び出しが同期戻りの後でも存在し続けることに同意します。


すべてではない、それほど単純ではない


上記から、非同期に変換された特定の同期関数への呼び出しが1つではなくいくつかあることが明らかになります。



特定の呼び出しごとに必要なコードをキャッチまたは分離し、そこで実行スレッドを転送する方法が必要です。 さらに、サブコールとリターンの間にデータを保存する機能が必要です。これは、各同期リターンの後に実行コンテキストが消えるためです。 以下を紹介します。



見て
 function A ( top ) { switch ( top.mark ) { case '#': break; case 'B': break; case 'C': break; } } 

すべてを適切に配置する:



見て
 function A ( top ) { switch ( top.mark ) { case '#': call(top, 'B', 'some_data'); break; case 'B': call(top, 'C', top.ret + 1); break; case 'C': back(top, {x: top.ret}); break; } } 

上記の例から、通常の同期関数が拡張されているだけで、非同期アクションを待ってから戻ることができることがわかります。 また、ステップへの分割とそれらの順次実行に気付くことができます。 これと、スタックではなく呼び出しツリーが使用されているという事実を考慮し、並列性を追加します。



見て
 function A ( top ) { switch ( top.mark ) { case '#': top.buf = []; call(top, 'B', 'some_data1'); call(top, 'B', 'some_data2'); call(top, 'B', 'some_data3'); break; case 'B': top.buf.push(top.ret); if ( !top.size ) { call(top, 'C', top.buf); } break; case 'C': back(top, top.ret); break; } } 

関数を実行する一般的な非同期プロセスの任意の順次ステップで、大規模な並列タスクを起動できることがわかりました。 また、このタスクの完了を待って結果を蓄積する機会。 それを改善しましょう。つまり、特定の関数のtop.ret結果は必ずしも必要ではないという事実を考慮し、1つのタスクで異なる関数を並行して実行できると便利です。



見て
 function A ( top ) { switch ( top.group['#name'] ) { case '#': top.buf = []; call(top, '#group1', 'B1', 'some_data1'); call(top, '#group1', 'B2', 'some_data2'); call(top, '#group1', 'B'3, 'some_data3'); break; case '#group1': top.buf.push(top.ret); if ( !top.size ) { call(top, '#group2', 'C', top.buf); } break; case '#group2': back(top, top.ret); break; } } 

実行中のいくつかのグループが終了するまで待機する別の機会を追加しました。それだけです:)



見て
 function A ( top ) { switch ( top.group['#name'] ) { case '#': top.listB = []; top.listC = []; call(top, '#group1', 'B1', 'some_data1'); call(top, '#group1', 'B1', 'some_data1'); call(top, '#group1', 'B2', 'some_data2'); call(top, '#group2', '1', 'some_data1'); call(top, '#group2', '1', 'some_data1'); call(top, '#group2', '2', 'some_data2'); break; case '#group1': if (top.ret) {top.listB.push(top.ret);} if ( !top.group['##size'] ) { back(top, {B: top.listB, C: top.listC}); } break; case '#group2': if (top.ret) {top.listC.push(top.ret);} if ( !top.group['##size'] ) { back(top, {B: top.listB, C: top.listC}); } break; } } 

まとめ


非同期関数の概念は上記で説明されており、完全に非同期の計算モデルを導入すると同時に、以下を可能にします。



私は、並列コンピューティングに重点を置いて、 この概念のかなり成功した実装を開発しました。 重要な機能は、スレッド(WebWorker)のサポートと、どのスレッドであるかに関係なく、非同期的に関数を呼び出す機能です。



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


All Articles