node-sync-ファイバーを使用したnodejsでの擬似同期プログラミング

node-fibersライブラリは長い間公開されており、nodejsおよびv8で素晴らしいファイバー / コルーチンのサポート、つまりyieldを使用する機能を導入しています。

並行して、非同期構文を簡素化するためのさまざまなオプションのトピックについて、nodejsグループで多くの 議論が行われました。



「ファイバー」を提供する機能に触発されて、非同期環境での開発をより便利にするノード同期ライブラリと、コードをより視覚的にするノード同期ライブラリを作成しました。



あらすじ


//   ,  callback    1  function someAsyncFunction(a, b, callback) { setTimeout(function(){ callback(null, a + b); }, 1000) } //    ,  Function.prototype.sync(), //     ,   call() //      ""  ,      var result = someAsyncFunction.sync(null, 2, 3); console.log(result); // "5"  1 
      
      







哲学



主なアイデアは、Function.prototype.sync()メソッドがデフォルトで任意の関数に組み込まれ、そのインターフェースがすべての既知の呼び出し()に対応することです。 同期ライブラリを接続することにより、追加のコードを記述せずに、非同期関数を同期的に呼び出すことができます。

擬似同期プログラミング-実際、Function.prototype.sync()はプロセス全体をブロックするのではなく、現在のスレッドのみをブロックするためです。 関数自体は非同期に実行され、結果を待つだけです(「yield」を使用)。 しかし同時に、コードは「同期的に」読み取られます。



node-syncは私にとって3つの重要な質問を解決します。

1.コールバックによる無限のインデントの免除(「スパゲッティコード」を排除)

2.正しいエラー処理

3.リファクタリングを必要としない既存のコード/ライブラリとの統合



1か月以上、アプリケーションでこのノード同期を使用してきましたが、移行は感知できませんでした。「擬似同期」方式で新しいコードを書き始めたところです。古いコードはそのままです。



ロックする



「ファイバー」の美しさは、待機中(収率)、プロセス全体ではなく、現在のスレッドのみがブロックされることです。 プロセス全体をロックする良い例は、fs.readFileSyncおよびその他の擬似同期関数です。



「ファイバー」を使用すると、グローバルブロッキングを回避し、同時にファイルを同期的に読み取ることができます。

 var fs = require('fs'), Sync = require('sync'); //    Sync(function(){ //   --> //   ,  Function.prototype.sync() var source = fs.readFile.sync(null, __filename); //     console.log(String(source)); })
      
      





このコードの違いは、fs.readFile.sync()からの応答を待っている間、アプリケーションが他の操作を静かに実行し続けることです。



エラー処理



nodejsで「hello worldアプリ」よりも深刻なことを書き込もうとした人は、おそらくエラー処理ルーチンに精通しているでしょう。 コールバック関数の公式設計に従っている場合、最初の引数は常にエラーを返します。 同時に、非同期環境でthrowを使用すると、イベントループ全体が落ちてしまいます。



正しいエラー処理を備えた非常にリアルな nodejsコード:

 //   -    , //  ,       function asyncFunction(callback) { var p_client = new Db('test', new Server("127.0.0.1", 27017, {})); p_client.open(function(err, p_client) { if (err) return callback(err); // <--  p_client.createCollection('test_custom_key', function(err, collection) { if (err) return callback(err); // <--  collection.insert({'a':1}, function(err, docs) { if (err) return callback(err); // <--  collection.find({'_id':new ObjectID("aaaaaaaaaaaa")}, function(err, cursor) { if (err) return callback(err); // <--  cursor.toArray(function(err, items) { if (err) return callback(err); // <--  //  = items callback(null, items); }); }); }); }); }); }
      
      





同期のみを使用する同じ機能。 エラー処理を考えると、その操作の結果は上記の関数と同じです。 呼び出された関数のいずれかがsync()が渡す自動コールバックでエラーを返す場合、このエラーは結果のコールバックに透過的に分類され、2番目の引数としてストリームに示されます。

 function syncFunction(callback) { //   Sync(function(){ var p_client = new Db('test', new Server("127.0.0.1", 27017, {})); p_client.open.sync(p_client); var collection = p_client.createCollection.sync(p_client, 'test'); collection.insert.sync(collection, {'a' : 1}); var cursor = collection.find.sync(collection, {'_id':new ObjectID("aaaaaaaaaaaa")) var items = cursor.toArray.sync(cursor); //  = items return items; }, callback) // <--    callback }
      
      





特別なFunction.prototype.async()メソッドを使用すると、この関数はさらにシンプルになります(上記の関数と同様に機能します)。

 var syncFunction = function() { var p_client = new Db('test', new Server("127.0.0.1", 27017, {})); p_client.open.sync(p_client); var collection = p_client.createCollection.sync(p_client, 'test'); collection.insert.sync(collection, {'a' : 1}); var cursor = collection.find.sync(collection, {'_id':new ObjectID("aaaaaaaaaaaa")) var items = cursor.toArray.sync(cursor); //  = items return items; }.async() // <--         
      
      







平行度



場合によっては、すべての結果を待っている間にいくつかの機能を並行して実行し、その後のみ続行する必要があります。 これにはSync.Parallelがあります。



 var Sync = require('sync'); // -  ,     function someAsyncFunction(a, b, callback) { setTimeout(function(){ callback(null, a + b); }, 1000) } //   Sync(function(){ //       //      ,       var results = Sync.Parallel(function(callback){ someAsyncFunction(2, 2, callback()); someAsyncFunction(5, 5, callback()); }); console.log(results); // [ 4, 10 ] //   var results = Sync.Parallel(function(callback){ someAsyncFunction(2, 2, callback('foo')); // assign the result to 'foo' someAsyncFunction(5, 5, callback('bar')); // assign the result to 'bar' }); console.log(results); // { foo: 4, bar: 10 } })
      
      





先日、私はlaverdet氏(v8のノードファイバーの作成者)と話し、彼は「未来」の非常に興味深いパラダイムを提案しました。 新しいFunction.prototype.future()メソッドを追加しました-並列処理にも使用できます:



 //   Sync(function(){ //  someAsyncFunction,     var foo = someAsyncFunction.future(null, 2, 2); var bar = someAsyncFunction.future(null, 5, 5); // foo, bar -     console.log(foo); // { [Function: Future] result: [Getter], error: [Getter] } //   ,    foo  bar console.log(foo.result, bar.result); // 4 10 -    ( ) })
      
      







設置



$ npm install sync

$ node-fibers my_file.js








ファイバーをサポートするには、「node」の代わりに「node-fibers」スクリプトを使用する必要があることに注意してください。



API



 var Sync = require('sync'); //  , fn - -,    Sync(fn) //  , fn - -, /   callback Sync(fn, callback) //    callback() Sync.Parallel(function(callback){ callback() //   ( ) callback('foo') //    }) //      / // obj - ,        Function.prototype.sync(obj, arg1, arg2) //       ,     //  / Future,    getter 'result' //    Future.result,      ,      // obj - ,        Function.prototype.future(obj, arg1, arg2) //      -  //  ,     // obj -  Function.prototype.async(obj)
      
      







まとめ



私は非常に正しいように思えるので、nodejsでこの方向をさらに発展させるつもりです。 このアイデアからインスピレーションを受けて、その開発に貢献してくれたら嬉しいです。



ライブラリ使用のかなり詳細なを確認することをお勧めします。

開発に参加するつもりなら、フォーク、ようこそ、 テストを忘れないでください。

また、 ベンチマークにスクリプトを追加しました。 他の誰かが繊維の速度をテストする方法についてアイデアを持っているなら、それはクールです。



egorFにブレーンストーミングを行い、トピック繊維に感染したという事実に感謝します:)



また、ノードファイバーに基づく他の ライブラリにも興味があるかもしれません。



All Articles