• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
Flow.js
 

Flow.js

on

  • 12,175 views

非同期プログラミングを驚きのシンプルさに ver 1.0.1 ...

非同期プログラミングを驚きのシンプルさに ver 1.0.1

and more.
http://uupaa.hatenablog.com/entry/2013/03/12/185555
http://uupaa.hatenablog.com/entry/2013/03/14/131556

Statistics

Views

Total Views
12,175
Views on SlideShare
12,017
Embed Views
158

Actions

Likes
54
Downloads
33
Comments
0

4 Embeds 158

https://twitter.com 140
http://s.deeeki.com 16
https://duckduckgo.com 1
https://kcw.kddi.ne.jp 1

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Flow.js Flow.js Presentation Transcript

    • Flow.jsA very simple way to wait for asynchronous processes. Version 1.0.1
    • JavaScript には非同期プログラミングが つきものです
    • XHR, onload, setTimeout, postMessage, addEventListener,DOMContentLoaded 沢山
    • 非同期プログラミングを支援するイディオムには、 Deferred/Promises, async/await などがありますが、
    • 今回紹介する Flow.js も、 非同期プログラミングを上手く扱う方法の1つです
    • Flow.js の 元となったニーズとウォンツ•  複数の非同期処理の完了を待ちたい •  ダウンロードの完了を待ちつつアニメーションしたい •  同期/非同期処理がちょっと離れた場所で混在しているが、 一方はカウンター、一方はコールバックの連鎖などで待機方法がバラバラ これらを画一的な仕組で待てないか •  いくつかの非同期処理をグルーピングし、それらの終了を待ちたい事がよ くあるが、毎回同じようなコードを書いている気がする•  シンプルな実装がほしい •  Deferred/Promiss を JavaScriptに詳しくない方に説明するのは骨が折れる•  運用で困らないようにしたい •  特定の環境に依存したり、頻繁に更新される 重厚なライブラリには依存できない(したくない)•  デバッグのしやすさも大事 •  どの非同期処理で止まっているのか原因を素早く特定したい
    • Flow.js の使い方
    • 導入npm または github から flow.js を取得してください。flow.js はコード単体で利用可能です。$ npm install flow_js var Flow = require("flow_js").Flow; または、 $ git clone git@github.com:uupaa/flow.js.git $ cd flow.js Browser: <script src="flow.js"></script>
    • 基本的な考え方•  Flowではユーザ側の同期/非同期処理を処理と呼びます Flowは処理が同期か非同期かを区別していません•  Flowは処理が終わるまで待機し、待機終了でcallbackを呼びます•  処理成功でpass()を、失敗でmiss()を呼んでください•  miss()を呼ぶと待機失敗で終了します。このデフォルトの動作を 変更する場合は、missable()で失敗可能な回数を指定します•  waits には待機する処理の数を指定します、 pass()の呼出回数がwaits以上になると、待機成功で終了しますvar waits = 3; var flow = new Flow(waits, callback); function callback(err, args) { ... } function userFunction(flow) { ...; flow.pass(); return }
    • 基本的な使い方new Flow(waits, callback)でインスタンスを作成します。waits に待機する処理の数を、callback には待機終了で呼び出す関数を指定します。callback(err, args)のerrには待機成功でnullが、待機失敗でErrorオブジェクトが渡されます。argsはpass(value)やmiss(value)で指定したvalueの配列です。// 2つ処理が終わるまで待機を行い、待機終了でcallbackを呼ぶ var flow = new Flow(2, callback); function callback(err, args) { ... } setTimeout(function() { flow.pass(); }, Math.random() * 1000); setTimeout(function() { flow.pass(); }, Math.random() * 1000);
    • 同期/非同期処理の成功で pass()同期/非同期処理の成功でpass()を実行します。pass()をwaits回数分呼び出すとFlowが待機成功となり、callback(null)を呼び出してFlowも終了します。var flow = new Flow(1, callback); flow.pass(); // 処理成功 -> 待機成功 -> callback(null);
    • 同期/非同期処理の失敗で miss()同期/非同期処理の失敗でmiss()を実行します。miss()を呼び出すとFlowが待機失敗となり、callback(Errorオブジェクト) を呼び出してFlowも終了します。var flow = new Flow(1, callback); flow.miss(); // 処理失敗 -> 待機失敗 -> callback(Error);
    • passとmissに値を渡すpass(value) や pass(value, key) と値を指定するとcallbackの第二引数 args から参照する事ができます。miss(value), miss(value, key) も同様です。key を指定すると args.key で value を検索できます。key を指定することで、 pass(value) の実行順を意識せずに値を受け渡すことができます。var flow = new Flow(3, function(err, args) { // [1, "value"] console.log(args["key"]); // -> "value" }); flow.pass(); // value を指定しない flow.pass(1); flow.pass("value", "key");
    • 非同期プログラミングを柔軟に、もっと便利にする使い方
    • 失敗可能な回数を設定する3回中1回成功すれば良い(2回まで失敗可能)といった、確率的な待機を行う場合に、missable(count) を実行します。count には失敗しても良い回数を指定します。missableで指定した回数以上 miss() を呼ぶと待機失敗となります。missable を設定した場合は、待機成功の条件が式1から式2になります式1: pass()実行数 >= waits 成立で待機成功で終了式2: pass()実行数 + miss()実行数 >= waits 成立で待機成功で終了var flow = new Flow(3, callback).missable(2); flow.miss(); // 処理失敗 -> missable=2 なので許容するflow.miss(); // 処理失敗 -> missable=2 なので許容するflow.miss(); // 処理失敗 -> missable=2 なので待機失敗 // -> callback(Error("fail"), [])
    • 待機処理数を継ぎ足すextend(waits)で動的に待機処理数の継ぎ足しが可能です。waits には追加する処理数を指定します。最初は1つしかなかった非同期処理が、内部で次々に非同期処理を呼び込むようなケースで利用します。var flow = new Flow(1, callback); flow.extend(1);flow.pass(); // 処理成功 -> waits=2 なので待機するflow.pass(); // 処理成功 -> 待機成功 -> callback(null, [])
    • Flowを強制終了するexit()でFlowを強制終了することができます。待機失敗で終了し、callback(Error("exit")) が渡されますvar flow = new Flow(2, callback); flow.exit(); // 強制終了 -> 待機失敗 // -> callback(Error("exit"), [])
    • Junction - 合流するcallbackに、別のFlowのインスタンスを指定することで、Junction(合流)を作ることができます。flow1とflow2のargsはjunctionのargsに引き継がれます。 flow1 junction callback flow2var junction = new Flow(2, callback); var flow1 = new Flow(2, junction).pass(1).pass(2); var flow2 = new Flow(2, junction).pass(3).pass(4); function callback(err, args) { // [ [1,2], [3,4] ] var values = Array.prototype.concat.apply([], args).sort(); console.log(values); // [1,2,3,4] }
    • Fork - 分岐するflows として分岐先を { name: 関数/Flow, ... } で指定し、fork(name)で、待機終了後の分岐先を指定できます。一番上の項目がデフォルトの分岐先になります。 flow fork("fork1")(default) fork("fork2")var flows = { "fork1": function(err, args) { ... }, "fork2": function(err, args) { ... } }; var flow = new Flow(1, flows); flow.fork("fork2"); // fork先を"fork2"に切り替え flow.pass(); // -> 処理成功 -> 待機成功 -> fork2(null, []) を呼出
    • 例外発生時のエスカレーション例外のハンドリングと対処はユーザ側で行い、miss()を呼び出してください。var flow = new Flow(2, callback); function someMethod() { try { // throw new TypeError("BAD_CASE"); flow.pass(); } catch (err) { flow.miss(); } }
    • 待機中のFlowをダンプするnew Flow(,, tag) を指定すると、Flow.dump()で待機中のFlowの一覧がダンプ可能になります。不具合で、いつまでも終了しないFlowの調査に役立ちます。tagはできるだけユニークな名前をつけるようにしてください。同じtagを再利用すると、以前の結果を上書きしてしまいます。var flow1 = new Flow(2, callback, "flowA"); var flow2 = new Flow(2, callback, "flowB"); Flow.dump(); { "flowA": { waits: 2, pass: 0, state: "progress", ... } }, { "flowB": { waits: 2, pass: 0, state: "progress", ... } } Flow.dump(true); // ダンプ後に内部情報をクリアします
    • 非同期に完了する処理をまとめた2つのグループ([A,B], [C,D])と、さらにそれら2つの完了を待ち合わせる合流処理の例です。 例: 非同期処理のネスト
    • 非同期に完了する2つのグループと、それらを待ち合わせるJunctionによる 非同期プログラミング 非同期処理グループ1処理A 処理B junction finishedCallback 非同期処理グループ2 ript処理C 処理D Ja vaSc
    • // Waiting for the completion of the asynchronous processes. function waitForAsyncProcesses(finishedCallback) { // Remaining count of the asynchronous processes. var waits1 = [A, B].length; // 2 var waits2 = [C, D].length; // 2 var waits3 = 2; function A() { setTimeout(function() { done_group1(); }, 10); } function B() { setTimeout(function() { done_group1(); }, 100); } function C() { setTimeout(function() { done_group2(); }, 20); } function D() { setTimeout(function() { done_group2(); }, 200); } function done_group1() { if (--waits1 <= 0) { junction(); } } function done_group2() { if (--waits2 <= 0) { junction(); } } function junction() { if (--waits3 <= 0) { finishedCallback(); } } vaSc ript A(), B(), C(), D(); Ja}
    • 非同期に完了する2つのFlowと、それらを待ち合わせるFlow(junction)による 非同期プログラミング group1 = new Flow(2, junction) 処理A 処理Bgorup1.pass() gorup1.pass() junction = new Flow(2) finishedCallback group2 = new Flow(2, junction) 処理C 処理Dgorup2.pass() gorup2.pass() Flo w.js
    • // Rewritten by Flow.js function waitForAsyncProcesses(finishedCallback) { // Create the flow instances, and build a combination of flow. var junction = new Flow(2, finishedCallback); var group1 = new Flow([A, B].length, junction); var group2 = new Flow([C, D].length, junction); function A() { setTimeout(function() { group1.pass(); }, 10); } function B() { setTimeout(function() { group1.pass(); }, 100); } function C() { setTimeout(function() { group2.pass(); }, 20); } function D() { setTimeout(function() { group2.pass(); }, 200); } A(), B(), C(), D(); } Flo w.js
    • 非同期に完了する2つのPromiseと それらを待ち合わせるPromiseによる 非同期プログラミング jQuery.when 処理A 処理Bdfd.promise() dfd.promise() jQuery.when finishedCallback jQuery.when 処理C 処理Ddfd.promise() dfd.promise() erred jQue ry.Def
    • // Rewritten by jQuery.Deferred function waitForAsyncProcesses(finishedCallback) { var promises1 = [A(), B()]; var promises2 = [C(), D()]; function A() { var dfd = jQuery.Deferred(); setTimeout(function() { dfd.resolve(); }, 10); return dfd.promise(); } function B() { var dfd = jQuery.Deferred(); setTimeout(function() { dfd.resolve(); }, 100); return dfd.promise(); } function C() { var dfd = jQuery.Deferred(); setTimeout(function() { dfd.resolve(); }, 20); return dfd.promise(); } function D() { var dfd = jQuery.Deferred(); setTimeout(function() { dfd.resolve(); }, 200); return dfd.promise(); } jQuery.when( jQuery.when.apply(null, promises1), jQuery.when.apply(null, promises2) red ).done(function() { finishedCallback() }); y.Defer} jQuer
    • Flow.js のまとめ•  Flow.js で書くとコードが短くなる •  速度劣化やメモリ増加も無視できる範囲です•  シンプルに同期/非同期プログラミングができる •  非同期処理のグルーピングやネストもできます•  ブラウザでもNode.jsでも動く •  Timer I/O 非依存(setTimeout 未使用) •  大抵の場所で使えます•  主要なメソッドは pass, miss の2つ •  易,枯,速,軽,小( flow.min.js は僅か1.2KBです)•  ググれる名前がある事が重要なので、名前をつけた •  Flow.js の原型は、数年前に実装済みだったりします
    • 気に入ったら使ってみてください。
    • ただし…
    • TypeScript Ver1.0では、C#由来の async/awaitが実装されるらしいので…
    • Flow.js は、それまでのつなぎのようなものかなと…
    • Link•  Flow.js •  https://github.com/uupaa/flow.js•  TypeScript loadmap •  http://www.slideshare.net/chack411/typescript-rev2-any-browser- any-host-any-os-open-source•  Promises/A idiom •  http://wiki.commonjs.org/wiki/Promises/A•  await/async idiom •  http://msdn.microsoft.com/en-us/library/vstudio/hh191443.aspx