非同期JavaScriptプログラミング-紛失

プログラマは当然のことながら、いくつかの機能を使用します。たとえば、次から次へとステップを実行するアルゴリズムを作成する場合など、順次プログラミングです。



ただし、システムで実行の単一スレッドをブロックすることは非常に悪い考えであるため、ブロッキング入力/出力または他の長い操作を使用するJavaScriptコードを記述する場合、シーケンシャルコーディングは問題外です。 解決策は、非同期コールバックを使用してアルゴリズムを実装することです。つまり、シリアルコードをいくつかのコールバックに分割します。



これで問題は解決しますが、シーケンシャルアルゴリズムを書くことができなくなり、重要なシリアルコードがコールバック関数のグラフに変換されます。



これは、非同期性を広範囲に使用する大規模なアプリケーションにとってさらに重要になっています。 非同期アクションにコールバック転送関数を使用することはあまり便利ではなく、戻り値を処理する必要があるため、複雑なコールバック転送プロセスを作成できます。



Node.JSは非同期コードに重点を置いているため、JavaScriptコミュニティはこれ、特にNode.JSコミュニティを認識しています。



CommonJSグループは、この呼び出しにPromisesの形式で応答しました。Promisesは、オブジェクトと対話するためのインターフェイスを提供することを目的としています。これは、非同期に実行されるアクションの結果であり、いつでも完了しない場合があります。

このようにして、さまざまなコンポーネントが非同期アクションのプロミスを返すことができ、コンシューマはプロミスを予測可能な方法で使用できます。 Promiseは、非同期での作業を支援する、より構文的に便利なレベルの言語拡張に使用される基本エンティティを提供することもできます。



階層化JavaScriptは、JavaScript言語のスーパーセットを使用してプログラミングを簡素化する別のアプローチです。 ただし、プログラミング言語を変更できない場合は、柔軟なAPIを使用して、シリアルコードをエミュレートできます。 このAPIが短い表記の使用を許可する場合、多くの場合、組み込みDSLと呼ばれます。



InfoQは、これらのAPIとDSLのリストを確認し、問題への取り組み方、設計原則、従うパラダイムなどについてクリエイターと話し合うことにしました。 そして、もちろん、これらの決定の制限について。



特に、InfoQはcに連絡しました。





InfoQ:あなたのライブラリはどのような問題に焦点を当てていますか? つまり、主にテンプレートコードの削除に焦点を当て、非同期I / Oの手動処理を回避するか、実行またはその他の機能を提供します(たとえば、実行結果を期待して複数のI / O呼び出しを同時に処理できるようにするため、など)。



Tim(Step) :Stepの目標は、定型コードを削除することと、非同期コードをより読みやすくすることです。 私たちのライブラリは非常に最小限であり、try..catchブロックとカウンタ変数を豊富に使用して手動で繰り返すことはできません。 私たちのライブラリの特徴は、各ステップでオプションの並列呼び出しのグループを持つ連続した呼び出しのチェーンの単純な形成です。



Will(Flow-js) :Flow-JSは、他のプログラミング言語に存在する継続またはファイバーに類似したJavaScriptコンストラクトを提供します。 実際には、マルチステップ非同期ロジックからいわゆる「ピラミッド」を破壊するために使用できます。 ネストされたコールバック関数のリテラルを直接使用する代わりに、実行スレッドの定義で指定された次の関数のコールバック関数として特別な値「this」を使用します。



Kris Zyp(ノード約束) :問題は、典型的なコールバックフロースタイル(連続転送スタイル)がインターフェイスの複雑さと機能ハンドラーと結果ハンドラーの混合を組み合わせていることです。 Promiseは、計算の最終的な完了をカプセル化し、クリーンな入力パラメーターを使用して関数/メソッドを実行できるようにします。一方、Promiseのような戻り値は結果を保存します。



ここここでこれらの原理をより詳細に説明しまし



イベントコンピューティングのカプセル化は、複雑な条件付きロジックを使用する場合でも、並列アクションと順次アクションの実行に最適です。 node-promiseライブラリには、これを簡単にするための関数が含まれています(promise-ioのすべての()およびstep()関数)



ちなみに、 promised-ioは実際にはノード約束の相続人です。 同じコアを持ちますが、promise-ioには、promiseスタイルのNodeJS I / O関数が含まれており、その中にプラットフォーム正規化レイヤーがあり、ブラウザーの同様の関数にアクセスできます。



Caolan(Async) :はい、主なタスクは定型コードを削除することです。 JavaScriptでコールバックを待機する関数を直列または並列で呼び出すためのコードは非常に詳細ですが、明らかです。 node.jsが非同期を処理するための基本的なメカニズムとしてプロミスからコールバックに切り替えた直後に、同じテンプレートを繰り返し使用していることがわかりました。 それらを別のライブラリに抽出する必要があることは明らかであるように思われました。



それ以来、依存関係に基づいてコールバックを実行できる、より複雑な機能を採用するようになりました。 ただし、ほとんどの場合、一般的な構造を開発者に任せるのはかなり低レベルのライブラリです。 しかし、JavaScriptはより機能的なプログラミングスタイルに適していることが多く、非同期バージョンのmap、reduce、filter、その他の一般的な機能をすぐに追加しました。 このスタイルで使用すると、ライブラリは本当にその強みを発揮し、継続オブジェクトまたはプロミスオブジェクトを使用せずに通常のコールバックに固執することができます。



Fabian(Async.js) :Async.jsは、JavaScriptの標準的な非同期テンプレートを簡素化しようとしています。 その主な目的は、一連の非同期関数を多くの同種のオブジェクトに適用することです。 それはforEach非同期関数から一連の一般的な概念へと成長しました。 これは、node.jsのファイルシステムと非同期で作業する場合に特に便利ですが、ライブラリはnode.jsに関連付けられておらず、他の同様のケースに使用できます。 このコードは、async.jsの使用方法を示しています。

async.readdir(__dirname) .stat() .filter(function(file) { return file.stat.isFile() }) .readFile("utf8") .each(function(file) { console.log(file.data); }) .end(function(err) { if (err) console.log("ERROR: ", err); else console.log("DONE"); });
      
      



このコードは、現在のディレクトリのすべての要素で動作します。 これは同種のオブジェクトのセットです。 ディレクトリ内の各アイテムに対して、一連の非同期操作が実行されます。 最初に、ファイルではないすべての要素が除外され、ファイルの内容がutf-8エンコードされたディスクから読み取られ、コンソールに出力されます。 すべての操作が完了すると、エラーインジケーターパラメーターを使用した最後のコールバック関数が呼び出されます。



AJ(FuturesJS) :非同期およびイベント駆動プログラミングについて話すのはかなり難しいです。



先物を主に作成しました:



  • ブラウザとサーバー(Node.JS)の両方に非同期制御フローの1つのライブラリを提供します。
  • コールバックとエラーハンドラーの呼び出しを処理するための高品質テンプレートの公開。
  • イベントが相互に依存するアプリケーション実行のフローを制御します。
  • マッシュアップなどの多くのリソースのコールバック処理。
  • モデルの使用やエラー処理など、プログラミングのベストプラクティスの使用を奨励する。




Futures.futureおよびFutures.sequenceは、定型コードの量を単純に減らし、ある程度の柔軟性を提供します。



Futures.joinは、オペレーティングシステムのスレッドに対してjoinが機能するのと同じ方法で)接続したり、(定期的に発生するイベントに対して)複数のfutureオブジェクトを同期したりできます。



Futures.chainifyを使用すると、Twitter Anywhere APIに似た非同期モデルを簡単に作成できます。



Isaac(slide-flow-control) :問題のスライドの目的は、OakJSミーティングで話し合うことができ、新しいアイデアを思い付かないことです。私はとても怠け者です。 基本的に、私はほとんど仕事をせずに、何をしたかを見せ、ビールを飲み、中華料理を楽しみ、面白い人とおしゃべりし、前向きな注意を払い、帰宅したかっただけです。 私にとって、ソフトウェアと生活の両方において、仕事量と利益の比は非常に重要です。 したがって、npmで使用する非常にシンプルなヘルパー非同期メソッドをまとめて、この機能のために「スライド」と呼ばれる一連のスライドに収まるようにし、このライブラリを導入しました。



このライブラリが重点を置いているもう1つのタスクは、独自のランタイムライブラリの作成がいかに簡単かを示すことです。 誰もが自分のライブラリが最高だと信じているので、基本的なパターンを人々に与えて構築させることが重要です。





InfoQ:ライブラリは、コンピュータサイエンスの科学者のアイデアを実装していますか?

Tim(Step) :直接-何もありません。



Will(Flow-js) :私にはわかりません。 Node.jsから管理される外部サービスへの多くの同期呼び出しを簡単に行うことができるビジネスロジックを作成した最初のヒットでした。



Kris Zyp(node-promise) :はい、もちろんです。 非同期設計に関するほとんどのコンピューターサイエンスの研究は、機能フローと関心の適切な分離のための最も適切なメカニズムとして、さまざまな形のPromiseを指しています。 「約束」という用語は、1976年に最初にダニエルP.フリードマンとデビッドワイズによって提案されました。 ウィキペディアの記事から約束に関するコンピューターサイエンスの豊かな歴史について学ぶことができます。



Caolan(Async) :私はコンピューターサイエンスの分野で経験がなく、純粋に実用的なベースでAsyncライブラリを実装しました。 JavaScriptの非同期性を一掃するために高階関数が必要になり、それを繰り返し使用すると、ライブラリに追加されました。



Fabian(Async.js)async.jsの実装はHaskell Monadsを漠然と連想させますが、これは偶然による可能性が高いです。





AJ(FuturesJS) :はい。 次の資料が最大の影響を及ぼしました。



非同期プログラミングの一番の長所は、より多くのモジュール式コードを書くことを自然に強制することであり、何らかの種類の非同期モデルがある場合は、モデルに常にパラメーターを渡し、モデルに関連するデータを渡さないという原則に従う必要があります、このモデルの外側。



Isaac(slide-flow-control) :1種類の継続パターンを使用します。 このトピックに関する多くのコンピューターサイエンスの研究が欠落していると思います。 月を指で突く。 そこに着くには、ロケットが必要です。 長い指は役に立ちません。 これに気付くと、より深い秘密が明らかになります。





InfoQ:ライブラリはエラー処理戦略を提供しますか? 例外のスローとどのように相互作用しますか?



Tim(Step) :いずれかのステップで例外がスローされると、例外がキャッチされ、エラーパラメーターとして次のステップに渡されます。 また、未定義の戻り値は、コールバックパラメーターとして次のステップに渡されます。 したがって、ステップは同じ構文を使用して同期または非同期にすることができます。



Will(Flow-js) :Flow-JSには組み込みの例外処理がありません。これは間違いなく弱点です。 Tim Caswellは、提供された各関数呼び出しをtry / catchブロックでフレーム化し、キャッチした例外をシーケンス内の次の関数に渡す「Step」というフローベースのモジュールを作成しました。



Kris Zyp(node-promise) :はい、 promiseは同期の実行スレッドと同等の非同期を提供するように設計されています。 JavaScript関数が例外をスローしたり、値を正常に返すことができるように、Promiseは正常な値またはエラー状態に解決できます。 呼び出されたメソッドに返された約束は、スローされた例外がキャッチされるまでスローされるように、エラーハンドラーがエラーを「キャッチ」するまでエラーを伝播できます。 ノード約束ライブラリはこの概念を正しくサポートしており、エラーハンドラを登録したり、キャッチされるまでエラーを配布したりするのを簡単にします(エラーを静かに飲み込む状況を排除するため)。 Promiseの直接同期等価物を使用すると、Promiseを使用したコード実行のフローは非常に読みやすくなります。



Caolan(Async) :非同期コードでの例外処理は、特にnode.jsのような非同期性の高い環境に慣れていない場合は、少し複雑になる可能性があります。 実際、私にとって、エラー処理は、この処理に適応するスタイルよりも重要です。 これはブラウザーで特に重要です。なぜなら、誤って例外を最上位レベルに誤って発生させ、ページ上のすべてのJavaScriptを強制終了する可能性があるからです。



これについて言えることはたくさんあります。 例外処理は直接実装し、できれば使い慣れたものにする必要があります。これにより、実装が容易になり、忘れた場合に明らかになります。 残念ながら、ブラウザのJavaScriptはこれを十分に支援しませんが、node.jsは両方の環境で簡単に使用できる簡単な規則を提供しました。



Asyncはこの特定の規則を採用し、最初のコールバック引数を使用して、プログラムの次のステップにエラーを渡します。 最初の引数がnull(または他のfalse値)の場合、無視できます。それ以外の場合、例外と見なされます。 可能な場合は、プロセスを高速化するために、Asyncライブラリによって簡単な方法で実行が行われます。 実行可能なコレクションの1つの関数が失敗すると、コレクション内の後続の関数は実行されません。



Fabian(Async.js) :Async.jsはnode.jsエラー処理規則を使用して構築されます コールバック関数の最初の引数は、エラーオブジェクト用に予約されています。 計算が失敗した場合、または例外が発生した場合、エラー/例外がコールバック関数の最初の引数として渡されます。 Async.jsは、APIを介して構成できる2つのエラー処理戦略をサポートしています。 エラーが発生した場合、セットの操作全体が停止してエラーハンドラーが呼び出されるか、エラー要素がスキップされます。



AJ(FuturesJS) :例外を非同期に「スロー」することはできないため、代わりに、コールバック関数の最初のパラメーターとして例外を渡すように求められます。



主なアイデアは、エラーを特定の時点で停止する代わりに、エラーに対してtry {} catch(e){}を実行し、エラーを渡すことです。 Futures.asyncify()は、主要な非同期環境で同期関数を使用するためにこれを行います。



以下に例を示します。
 (function () { "use strict"; var Futures = require('futures'), doStuffSync, doStuff; doStuffSync = function () { if (2 % Math.floor(Math.random()*11)) { throw new Error("Some Error"); } return "Some Data"; }; doStuff = Futures.asyncify(doStuffSync); doStuff.whenever(function (err, data) { if (err) { console.log(err); return; } console.log(data); }); doStuff(); doStuff(); doStuff(); doStuff(); }());
      
      







Isaac(slide-flow-control) :例外をスローしないでください! 絶対に! 例外を投げることは悪です。 これをしないでください。 コールバック関数が呼び出されると、最初の引数はエラーまたはnullになります。 これがエラーの場合、それを処理するか、コールバック関数を処理に渡します。 エラーを最初のパラメーターとしてコールバック関数に渡し、エラーを通知します。





InfoQ:インスピレーションはありましたか、またはライブラリはどのような影響で作成されましたか(たとえば、F#ワークフロー、Rx(Javascriptのバージョン)、またはその他のプロジェクト)。



Tim(Step) :はい、スタイルはflow-jsプロジェクトから直接借用されました。



Will(Flow-js) :そうではありません。 それは私に起こった最初の決断に過ぎませんでした。



Kris Zyp(ノード約束) :ノード約束ライブラリは、Mark Millerのプログラミング言語Eとその中でのpromiseの使用、Tyler Close ref_sendライブラリ、Kris KowalライブラリQ、Neil Mix NarrativeJSライブラリ、Twistedフレームワークでの遅延実装の影響を受けました。 Dojo、および他の多くのライブラリ。



Caolan(Async) :F#やRxを使用しなかったので、これらのプロジェクトに対する態度を表明できないと思います。 ただし、JavaScriptでプログラミングするための優れた機能ライブラリであるUnderscore.jsからインスピレーションを得ました。 反復子がアンダースコアから使用する関数のほとんどは、コールバックと非同期で動作を開始するように変更され、非同期ライブラリに統合されました。



Fabian(Async.js) :特定のAPI呼び出しチェーンはjQueryに触発されました。 私の目標の1つは、node.jsファイルシステムモジュールにjQueryのようなAPIを提供することでした。 Pythonスタイルのジェネレーターも非常に影響力があります。 チェーン内の各要素は、チェーンの後続の要素で使用できる値を生成します。 操作全体は、チェーン内の最後の要素によって呼び出され、チェーンを通じて値を「プル」します。 この観点から、async.jsはソースによって値がプッシュされるjQueryおよびRxとは異なります。 この「プル」システムにより、すべての値を遅延計算することが可能になり、無限数の値(たとえば、すべて偶数)を返すジェネレーターを作成することもできます。



AJ(FuturesJS) :すぐではありません。



FacebookとAmazonを使用してマッシュアップサイトを構築しましたが、2つのリソースから作成されたモデルの操作方法がわからなかったため、最初の試みはミッシュマッシュでした(当時JavaScriptにあまり慣れていませんでした。 「WTFJS」のスタイルで試行錯誤し、少しjQueryを使用してDOMでの苦痛な作業を簡素化しました)。



そのため、必要に応じてデータが既に存在することを想定するよりも、データを取得するのに時間がかかると想定する方が常に容易であることがわかりました。そして、チェーン全体を特定のセットまで下から再編成する必要があることに気付きましたデータを非同期で処理できます。



私はいくつかの異なる方法を使って試し、失敗し、半分習得した後、幸運なことに、 私の地元のJavaScriptユーザーグループのメーリングリストの誰かがCrawfordのJSに関する一連の講義に言及しました 。 シリーズ全体を見てから(3番目のセクションを少なくとも3回見ました)、ついに非同期プログラミングの「問題」(または機能)を管理する方法を理解し始めました。 次に、クロフォードのスライドを見つけて、彼が出発点から引用した約束の例から始めました。



後でNode.JSで遊んで、その結果、エラー処理戦略を変更しました(しかし、数日前にドキュメントを更新しただけです)。 今週の日曜日にリリースするFutures 2.0では、ブラウザーで使用するNode.JSからEventEmitterも追加しました。



Isaac(slide-flow-control) :なし。 これは、コールバックを使用するためにNodeJSで思いついたパターンに触発されたと思います。 私は完全さのサポーターです。なぜなら、混乱してトイレに行くことなく、これが浴室だと思って、1種類以上(または、良い日に2杯のコーヒーを)を覚えるほど頭が良くないからですそして、すべての服のような匂いを取得します...しかし、あなたはアイデアを得る。



どこでも同じ種類のものを使用します。 以上です。 最後の引数としてコールバックを取る関数。 エラーメッセージを最初の引数として受け取るコールバック関数、またはすべてがうまくいけばnull / undefinedを受け取ります。 スライドは、このスキームを使用して多くのことを簡単に実行できるようにするヘルパー関数のほんの一部です。





InfoQ:たとえば、ライブラリをより簡潔にするなど、ライブラリを改善できる新しい機能やJavaScript言語の変更はありますか?



Tim(Step) :おそらく、言語のセマンティクスに重大な変更がない限りではありません。 coffeescriptのようなプリプロセッサが構文の助けになるかもしれませんが、ほとんどの場合、バニラJavaScriptを使用することをお勧めします。



Will(Flow-js) :私の意見では、JavascriptはRuby 1.9のファイバーのようなものに必死です。 私は将来のプロジェクトのためにNode.jsで作業するのに多くの時間を費やしましたが、ある時点で、非同期プログラミングは常に私の脳を溶かします。 管理しやすくするためのツールはたくさんありますが、それを証明することはできませんが、Flow-jsのようなライブラリがたくさんあるということは、Javascriptが並行プログラミングに実際に失敗している証拠にすぎないと感じています。



Nodeの目標の1つは、V8 JavaScriptエンジンのコアへの変更を回避することでしたが、私が知る限り、 Asanaのメンバーは問題なくカーネルにファイバーを追加しました



Kris Zyp(ノード約束) :はい、最近、ジェネレーターに似た単一フレームまたは小さな継続に関する議論がありました。これは、分岐とスレッドループを複雑にするコールバックの必要性を回避するのに役立ちます。 これをpromiseと組み合わせて使用​​すると、非常にシンプルで読みやすい非同期コードを作成できます。



ちなみに、もう1つの注意点-node-promiseライブラリはhttp://wiki.commonjs.org/wiki/Promises/A仕様も実装しています。つまり、Dojoと、おそらく将来のJQuery Promiseと対話できます。



Caolan(Async) :Asyncは、現在の形式で言語を最大限に活用するように設計されました。 JavaScriptの上に新しい言語を作成しようとせずに、そのまま動作します。



ただし、JavaScript 1.7にyieldを追加すると、将来のプロジェクトに役立つアプリケーションがいくつかあるかもしれません。 yieldを使用すると、いくつかのTwistedのような関数をJavaScriptに移植して、より同期的なコーディングスタイルを実現できます。 これは私の同僚がWhorlで研究していることですが、このプロジェクトは開発をやめたようです。





Fabian(Async.js)Mozillaサポートするジェネレーターとイテレーターを標準化すると、async.jsコードをより簡潔にし、非同期の問題を簡素化できます。



AJ(FuturesJS) :ライブラリ内で最も頻繁に繰り返されるコードは、ブラウザーとNode.JSで同じコードを機能させるコードです。 この問題を解決するためにいくつかのライブラリ(テレポートなど)が作成されたことは知っていますが、まだそれらのライブラリを使用したことはありません。 もちろん、非同期のrequireが言語に組み込まれている場合は便利です。



私の観点からすると、JavaScriptのような自然な非同期性を備えた言語は、言語の中核に組み込まれた先物に似たものを持つべきです。



CommonJSにはサーバーの約束を標準化するためのいくつかの提案がありますが、それらの焦点はデータのプライバシーにありますが、将来はフロー制御、エンド開発者による使いやすさ、およびブラウザーの互換性に焦点を当てています。



Isaac(slide-flow-control) :なし。 私のフロー制御ライブラリは最高です。 これは私の自己に直接関係しているので、他の方法で改善することはできません。したがって、外部の影響はそれを私のものよりも小さくし、それによりあまり良くありません。 経験を積むには、ライブラリを書くことをお勧めします。 あなたがそれを書くとすぐに、彼女が最高であることがわかります。 他のライブラリが何かがより良いと考えている場合、エディタに急いで戻って恥を隠し、すべてのアイデアをすぐに再発明できますが、以前とは少し異なる方法で、あなたは知っていますあなたの新しいものが今最高であることをあなたの心の中で。




All Articles