Gleyzer両氏によって共同開発されました。2019で、同言語の発表を行いました。InfoQはPurdy氏に言語と、氏らが解決を目指した問題について聞きました。設計/アーキテクチャアジャイルマニュフェスト:ソフトウェアアーキテクトの視点ソフトウェアアーキテクトの役割と責任はアジャイルソフトウェア開発のためのマニフェストの価値と矛盾していると見えますが、優れたアーキテクトはアジャイル開発チームをサポートするテクニックを見つけています。Streamingは、ストリーミングデータのためのSQLライクなインターフェースを提供します。Streamsによって、Redisがストリーミングデータを複数のプロデューサとコンシューマの間で消費、保持、配信することができるようになります。ソフトウェア開発のマニフェストたゆまず息もつかずに市場に突進することで、会社を静かに消滅の崖っぷちへと追い込みます。それは設計してコードを書く方法を再考する時です。簡単さへの投資は、速さへの投資です。簡単さは知的財産の主鉱脈でもあります。競争上の優位性は、一度失ったら、もう一度手に入れることはほとんど不可能です。Buytaerts氏が過去10年間にわたって、企業がDevOps採用のハイプサイクルをくぐり抜け、変革に対する誤解を解消する過程を支援してきた経験について語ります。原文(投稿日:2011/01/09)へのリンクプログラマはものごとを順番にやるようなアルゴリズムを書いて、シーケンシャルにプログラミングするのを当然のことだと思っている。JavaScriptでブロッキングI/Oや時間を要する操作のあるコードを書いているなら、シーケンシャルなコードというのは問題外だ。システムで唯一のスレッドをブロックするのは、非常にまずいことだからだ。これを解決するには非同期コールバックを使ってアルゴリズムを実装すればよい。シーケンシャルなコードを複数のコールバックに展開するのだ。関連スポンサーコンテンツこれで問題は解決と言いたいところだが、これはシーケンシャルなアルゴリズムが書けなくなることを意味している。重要なシーケンシャルなコードを書く代わりに、コールバックのグラフを書くことになる。非同期性を多用している大規模アプリケーションでは、これはさらに重要になる。コールバック渡しを使っても、うまい具合に非同期アクションを組み立てられず、戻り値を処理するためにコールバックをたらい回しにするという複雑なフローになるおそれがある。JavaScriptコミュニティ、特にNode.jsコミュニティは、このことに気づいている。Node.jsでは非同期コードが重要になるためだ。これに対して、CommonJSグループはPromiseという形でこれに答えている。これは任意の時点で、完了しているかもしれないし完了していないかもしれない、非同期に実行されるアクションの結果を表現したオブジェクトとのインターフェイスを提供する。この方法では、さまざまなコンポーネントが非同期アクションのためのpromiseを返すことができ、コンシューマは予測可能な形でそのpromiseを利用できる。Promiseは非同期性を支援するために構文上便利な言語レベル拡張のために利用される基本エンティティを提供することもできる。JavaScriptはこれとは別のアプローチをとっており、JavaScript言語のスーパーセットを提供することで、この問題を解決している。使う言語を切り替えられないのなら、とるべき道はシーケンシャルなコードをエミュレート可能な、柔軟なAPIを使うことだ。そのAPIに簡潔な記法が許されている場合、組み込みDSLと呼ばれることが多い。うべきパラダムなどにどうアプローチしたのか、そして、それぞれの限界がどこにあるのかについて質問した。InfoQでは以下の方々にコンタクトした。Stepが目標にしているのは、お決まりのコードをなくすこと、そして、非同期コードの可読性を高めることです。これは極めて最小限のもので、いずれも大量のtry..catchブロックとカウンタ変数を使えば手作業でやれることです。Stepが提供するのは、各stepでオプションの並列グループを使ったシリアルなアクションを簡単にチェーンできることです。ロジックから、いわゆる「ピラミッド」を取り除くのに使えます。コールバック関数リテラルを直接ネストするのではなく、フロー定義における次の関数へのコールバックとして特別な"this"値を使います。問題なのは、通常のコールバックスタイルの実行フロー(継続渡しスタイル)では、関数の入力と出力の処理がまざって、複数の関心事をひとつのインターフェイスに融合していることです。Promiseは計算の最終的な完了をカプセル化することで、関数/メソッドが純粋に入力パラメータで実行されるようにします。その一方で、出力である返されたpromiseが結果を保持します。ここやここで、もう少し詳しく説明しています。最終的な完了をカプセル化することにより、複雑な条件分岐があるような並列なシリアルなアクションをまとめる場合にも、promiseは非常にうまく機能します。node-promiseライブラリには、これを簡単にする関数が含まれています(promised-ioのall()関数とstep()関数)。ご参考までに、promised-ioはnode-promiseの代替品の一種です。コアとなるpromiseライブラリは同じですが、promised-ioには、Node.JSのIO関数のpromiseスタイル版と、ブラウザで同様の関数にアクセスするために使うプラットフォーム正規化が含まれています。はい、大部分はお決まりのコードをなくすことです。JavaScriptにおいて、関数を順番に、もしくは、並列に呼び出して、コールバックを待つというコードは、かなり冗長ですが明確です。node.jsが非同期コードを処理する基本手段としてpromise上でのコールバックを採用して間もなく、私は何度も同じパターンを使っていることに気づきました。そこで、明らかにこれらは別のライブラリに抽出するべきだと考えたのです。以来、依存関係に基づいてコールバックをまとめられるようにするなど、さらに複雑な機能を取り込んで成長してきました。その大部分において、これはかなり低レベルのライブラリであり、全体の構成については開発者の手に委ねられています。JavaScriptには関数プログラミングスタイルや、map、reduce、filterといった便利なものの非同期バージョンの追加が適していると思っています。このライブラリはそう使われたときに真の効果を発揮して、継続やpromiseオブジェクトを使わずに従来通りのコールバックが使えるようにします。Async.jsはJavaScriptでよく見られる非同期パターンを単純化しようとしたものです。目的は、オブジェクトの一様な集合に一連の非同期関数を適用することです。これは非同期のforEach関数から進化して、その概念を一般化したものです。これはnode.jsの非同期ファイルシステムAPIを扱うときに特に役立ちます。node.jsに縛られているわけではなく、同じような問題すべてに使えます。async.jsでどんなことができるか、少しお見せしましょう。})このコードは現在のディレクトリにあるすべてのアイテムに対して操作するものです。これがオブジェクトの一様な集合になります。各アイテムに対して、一連の非同期オペレーションが実行されます。ファイルではないアイテム(例えばディレクトリ)をフィルタリングし、次にディスクからそのアイテムを読み出して、コンソールに表示します。すべてのアイテムが処理されると、最後のコールバックが呼び出され、最終的なエラーを表示します。非同期のイベント駆動プログラミングを論理的に考えるのはやや難しいことです。私は主に以下を目的としてFuturesを作りました。ブラウザとサーバーサイド(Node.JS)で使うための、単一の非同期コントロールフローのライブラリを提供するコールバックとエラーバック処理に一貫性のあるパターンをもたせるあるイベントが別のイベントに依存するという状況で、アプリケーションのフローをコントールするマッシュアップのように、複数のリソースに関するコールバックを処理するモデルの利用やエラー処理など、すぐれたプログラミング・プラクティスを促すFutures.futureとFutures.sequenceは、よく見かける大量のお決まりのコードを削減して、多少柔軟性をもたせます。Futures.joinは、複数のfuturesをjoinしたり(スレッドのjoinがやるのと同様)、(時々発生するイベントを)同期します。APIのような非同期モデルの作成を簡単にします。meetupで話すネタを必要としていたことです。新規のアイデアを思いつきたかったわけではありません。私はほんとに、なまけ者なんです。ほとんど仕事をせずに、目立って、ビールを飲んで、中華料理をつまんで、おもしろい人たちと付き合って、ちょっと注目を浴びて、そうして帰宅したいと、いつも思っているんです。私にとって、ソフトウェア、そして、人生における仕事と報酬の比率は極めて重要なことです。npmで使えて、スライドに収まる、極めて単純な非同期ヘルパー関数を作って、その目的を達成したわけです。それに遠慮して、"slide"という名前をつけて、発表したんです。slideが解決するもうひとつの問題は、自作のフローコントロールライブラリがとても簡単に書けることをお見せすることです。みんな自分で書いたのが一番好きなんです。基本パターンをいくつか提供して、みんなをクリエイティブにさせるのは筋が通っているでしょう。直接的にはないですね。私が知る限りありませんね。これは私がNode.jsで扱えるビジネスロジックを作るときに使う、最初のスタブにすぎず、外部サービスへの複数の同期呼び出しの世話をするものでした。非同期設計におけるコンピュータサイエンス研究の多くは、関数フローや関心事の分離に最も適切なメカニズムとして、さまざまな形でpromiseを取り上げています。Wise氏によるもので、1976年にまでさかのぼります。コンピュータサイエンスにおけるpromiseの歴史については、Wikipediaの記事を読むとよいでしょう。私にはコンピュータサイエンスのバックグラウンドがありません。Asyncライブラリは純粋に実践に基づいて実装していきました。非同期JavaScriptを少し整理するのに高階関数が必要になると、私はそれを何度も使って、ライブラリに取り入れたのです。async.jsの実装は、Haskellのモナドに間接的に似ていますが、これは偶然です。影響を受けたのは以下からです。IIIGoogleの怠惰なプログラマーのためのセキュアコンピューティングガイド非同期プログラミングで一番よいことは、自然にモジュール化されたコードが書けるようになることです。非同期モデルのようなものがあれば、常にパラメータを渡したり、そのモデルの外部にあるモデルに属するデータを渡さないといった原則に従うことを強制することができます。いわゆる継続パターンを実装しています。多くのコンピュータサイエンス研究はポイントを見失っていると思ってます。月を指している指のようなものです。そこへ行くにはロケットが必要なのです。指している指など役に立ちません。これが完全に理解できたら、深い謎が正体を見せるでしょう。例外が任意のstepで投げられると、それはキャッチされて次のstepにエラーパラメータとして渡されます。未定義でない戻り値は次のstepにコールバック値として渡されます。このように、stepは同期にも非同期にも同じシンタックスを使えます。Flow-JSには組み込みの例外処理はありません。Caswell氏は"Step"というフローに基づいたモジュールを書いています。これは与えられた各関数への呼び出しをtry/catchブロックにラップして、キャッチした例外を順番に次の関数へと渡しています。はい、promiseは同期フローと同じものを非同期にも提供するよう設計されています。JavaScript関数がエラーをスローするか、もしくは成功して値を返すのと同様に、promiseも成功した値かエラー状態へと解決します。呼び出し元へ渡されたpromiseは、エラーハンドラがエラーを「キャッチ」するまで、エラーを伝播させます。node-promiseライブラリはこの概念をうまく維持しながら、エラーハンドラを登録したり、(エラーが黙って抑制されるのを避けるため)他でキャッチされるまでエラーを伝播させるのを簡単にします。promiseに直接同期と同じものをもたせることで、コードフローに対する既存の理解でpromiseを論理的に考えやすくしています。非同期コードにおける例外処理は少々トリッキーです。node.jsなどの非同期環境になじみがなければ、特にそうです。私にとって、どんなスタイルでエラーを処理するかよりも、実際にエラーを処理することの方が重要です。ブラウザの場合、すぐに例外が一番上まであがってきて、そのページのJavaScriptを壊してしまうので、これは特に重要です。従うべき決まりとして、いろいろなことが言われています。例外処理はわかりやすく、できればなじみがあり、そのため、実装するのが簡単で、忘れてもすぐにわかる必要があります。残念ながら、ブラウザにおけるJavaScriptはあまり役に立ちません。node.jsはどちらの環境でも使えるシンプルな決まりを提供しています。Asyncライブラリは、まさにこの、エラーをプログラムの次ステップに渡すために、コールバックの最初の引数を使う、というスタイルを採用しています。最初の引数がnull(あるいは他の「偽」な値)であれば、それは無視されます。なければ、例外として扱われます。可能であれば、高速化のためにAsyncライブラリ内で実行をショートカットします。順番に動作している関数のひとつがそのコールバックにエラーを渡すと、後に続く関数は呼び出されません。Async.jsにはnode.jsのエラー処理の決まりを組み込んでいます。コールバックの最初の引数にはエラーオブジェクトが保存されます。計算が失敗したり、例外がスローされると、エラー/例外がコールバックの最初の引数に渡されます。Async.jsは2つのエラー処理戦略をサポートしていて、API経由で設定可能になっています。エラーが発生したときには、すべての動作をストップしてエラーコールバックを呼び出すか、もしくは、失敗したエレメントをスキップします。例外は非同期に「スロー」されないため、代わりにユーザーがコールバックの最初の引数として例外を渡すことを推奨しています。Futures.asyncify()は、大部分が非同期な環境において同期関数を使えるようにしてくれます。以下に例をあげましょう。