知らないノード:主な機能の簡単な概要





著者からのコメント

この記事は新しいものですが、新機能に関するものではありません。 それは中核、つまりプラットフォームに関するものであり、単にうなり声やwebpackを単に使用する多くの人が疑わないかもしれないという事実についてです。



詳細を読む:



ラムキンのコメント:

habrahabr.ru/company/mailru/blog/283228/#comment_8890604



アイディッツのコメント:

habrahabr.ru/company/mailru/blog/283228/#comment_8890476



Suvitrufコメント:

habrahabr.ru/company/mailru/blog/283228/#comment_8890430





この投稿のアイデアは、Kyle Simpsonの一連の書籍「 You Do n't Know JavaScript 」に触発されました。 彼らはこの言語の基礎を学ぶ良いスタートです。 Nodeは、この記事で説明する小さな違いを除いて、ほぼ同じJavaScriptです。 以下のすべてのコードは、 リポジトリからcode



フォルダーからダウンロードできます。



Nodeに煩わされるのはなぜですか? NodeはJavaScriptであり、JavaScriptはほぼすべての場所で使用されています! 開発者の大多数がNodeを完全にマスターすれば、世界は良くなるでしょう。 より良いアプリケーション、より良い生活!



この記事は、Nodeの最も興味深いコア機能の現実的な外観です。 記事のキーポイント:



  1. イベントループ:ノンブロッキングI / Oを実装するための重要な概念の更新
  2. グローバルオブジェクトとプロセス:詳細情報の取得方法。
  3. イベントエミッター:イベントベースのパターンの集中的な紹介
  4. ストリームとバッファ:データを操作する効率的な方法
  5. クラスター:プロのようなフォークプロセス
  6. 非同期エラー処理: AsyncWrap、Domain、およびuncaughtException
  7. C ++のアドオン:カーネルで独自の開発を行い、C ++で独自のアドオンを作成する


イベントループ



Nodeの基礎となるイベントループから始めましょう。





Node.jsの非ブロッキングI / O



このサイクルにより、I / O操作の実行と並行して他のタスクを操作できます。 NginxとApacheを比較してください。 I / Oのブロックは安くないため、Nodeは非常に高速で効率的です。イベントループのおかげです。



Javaで保留中のprintln



関数のこの単純な例を見てください。



 System.out.println("Step: 1"); System.out.println("Step: 2"); Thread.sleep(1000); System.out.println("Step: 3");
      
      





これはNodeコードに匹敵します(完全ではありませんが):



 console.log('Step: 1') setTimeout(function () { console.log('Step: 3') }, 1000) console.log('Step: 2')
      
      





これは同じではありません。 非同期操作の観点から考え始めます。 Nodeスクリプトの出力は1、2、3です。 しかし、「ステップ2」の後にさらに式があれば、最初にそれらが実行され、それからsetTimeout



関数のコールバックが実行されます。 このスニペットを見てください:



 console.log('Step: 1') setTimeout(function () { console.log('Step: 3') console.log('Step 5') }, 1000); console.log('Step: 2') console.log('Step 4')
      
      





彼の作業の結果はシーケンスsetTimeout



ます。理由は、 setTimeout



がイベントループの将来の期間にコールバックを配置するためです。



イベントループは、 for … while



ような無限ループと考えることができます。 彼は、現在も将来も、何もすることがないときにだけ立ち止まります。





ブロッキングI / O:マルチスレッドJava



イベントループにより、システムはより効率的に動作し、アプリケーションは高価なI / O操作の完了を待っている間に他のことを行うことができます。





Node.jsの非ブロッキングI / O



これは、オペレーティングシステムのスレッドを使用するより一般的な同時実行モデルとは対照的です。 ネットワークフローモデル(スレッドベースのネットワーク)は非常に非効率的で、使用が非常に困難です。 さらに、Nodeユーザーはプロセスを完全にブロックすることを恐れないかもしれません-ここにはロックがありません。



ところで、Node.jsでは、ブロッキングコードを作成できます。 この簡単なスニペットを見てください。



 console.log('Step: 1') var start = Date.now() for (var i = 1; i<1000000000; i++) { // This will take 100-1000ms depending on your machine } var end = Date.now() console.log('Step: 2') console.log(end-start)
      
      





もちろん、通常、コードには空のループはありません。 他の人のモジュールを使用する場合、同期を検出することがより困難になる可能性があります。つまり、コードをブロックします。 たとえば、メインモジュールfs



(ファイルシステム)には2つのメソッドセットが付属しています。 各ペアは同じことを行いますが、方法は異なります。 fs



モジュールのブロックメソッドには、名前にSync



という単語が含まれています。



 var fs = require('fs') var contents = fs.readFileSync('accounts.txt','utf8') console.log(contents) console.log('Hello Ruby\n') var contents = fs.readFileSync('ips.txt','utf8') console.log(contents) console.log('Hello Node!')
      
      





このコードの実行結果は、Node / JavaScriptの初心者でも完全に予測可能です。



 data1->Hello Ruby->data2->Hello NODE!
      
      





しかし、非同期メソッドに切り替えると、すべてが変わります。 ノンブロッキングコードの例を次に示します。



 var fs = require('fs'); var contents = fs.readFile('accounts.txt','utf8', function(err,contents){ console.log(contents); }); console.log('Hello Python\n'); var contents = fs.readFile('ips.txt','utf8', function(err,contents){ console.log(contents); }); console.log("Hello Node!");
      
      





実行には時間がかかるため、 contents



は最後に表示され、コールバックにも含まれます。 ファイルを読み取った後、イベントループがそれらに移動します。



 Hello Python->Hello Node->data1->data2
      
      





一般に、イベントループとノンブロッキングI / O操作は非常に強力ですが、多くは慣れていない非同期コードを記述する必要があります。



グローバルオブジェクト



開発者がブラウザベースのJavaScriptまたは別の言語からNode.jsに切り替える場合、次の質問があります。





これを行うには、特定のプロパティを持つグローバルオブジェクトがあります。 それらのいくつかを次に示します。





よくある疑いもあります-ブラウザJavaScriptのメソッド:





各グローバルプロパティには、 global.process



代わりにglobal.process



記述process



だけで、大文字で入力された名前GLOBAL



、または名前をまったく付けずにアクセスできます。



プロセス



プロセスオブジェクトには多くの情報が含まれているため、別の章に値します。 以下にそのプロパティの一部を示します。





いくつかの方法:





難しい質問:誰が好きで、誰がコールバックの本質を理解していますか?



誰かが彼らと「恋をしている」ため、彼はhttp://callbackhell.comを作成しました。 この用語がよくわからない場合は、次の図をご覧ください。



 fs.readdir(source, function (err, files) { if (err) { console.log('Error finding files: ' + err) } else { files.forEach(function (filename, fileIndex) { console.log(filename) gm(source + filename).size(function (err, values) { if (err) { console.log('Error identifying file size: ' + err) } else { console.log(filename + ' : ' + values) aspect = (values.width / values.height) widths.forEach(function (width, widthIndex) { height = Math.round(width / aspect) console.log('resizing ' + filename + 'to ' + height + 'x' + height) this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) { if (err) console.log('Error writing file: ' + err) }) }.bind(this)) } }) }) } })
      
      





コールバック地獄は読みづらく、ここで間違いを犯すことは簡単です。 それでは、コールバックを使用しない場合、開発の観点からのスケーリングにはあまり便利ではないのに、どのようにモジュールに分割し、非同期コードを整理しますか。



イベントエミッター



イベントエミッターは、コールバックの地獄、または運命のピラミッドに対処するために使用されます。 彼らの助けを借りて、イベントを使用して非同期コードを実装できます。



要するに、イベントエミッタは、誰でも聞くことができるイベントのトリガーです。 Node.jsでは、イベントごとに、エミッターがコールバックをハングできる文字列名が割り当てられます。



エミッターとは:





エミッタを使用するには、モジュールをインポートし、オブジェクトのインスタンスを作成する必要があります。



 var events = require('events') var emitter = new events.EventEmitter()
      
      





次に、イベントレシーバーを接続し、イベントをアクティブ化/送信できます。



 emitter.on('knock', function() { console.log('Who\'s there?') }) emitter.on('knock', function() { console.log('Go away!') }) emitter.emit('knock')
      
      





EventEmitter



使用して、彼から継承して有用なことEventEmitter



しましょう。 月ごと、週ごと、または毎日、クラスを定期的に実装する必要があるとします。 このクラスは、他の開発者が最終結果をカスタマイズできるように十分な柔軟性が必要です。 つまり、作業の最後に、誰でもクラスに何らかのロジックを配置できる必要があります。



この図は、イベントモジュールからの継承を使用してJob



作成し、 done



イベントレシーバーを使用してJob



クラスの動作を変更する方法を示しています。





Node.jsのイベントエミッター:オブザーバーパターン



Job



クラスはそのプロパティを保持しますが、同時にイベントを受け取ります。 プロセスの最後で、 done



イベントを起動するだけです。



 // job.js var util = require('util') var Job = function Job() { var job = this // ... job.process = function() { // ... job.emit('done', { completedOn: new Date() }) } } util.inherits(Job, require('events').EventEmitter) module.exports = Job
      
      





最後に、 Job



の動作を変更します。 done



を渡すと、イベントレシーバーをアタッチできます。



 // weekly.js var Job = require('./job.js') var job = new Job() job.on('done', function(details){ console.log('Job was completed at', details.completedOn) job.removeAllListeners() }) job.process()
      
      





エミッタには他の可能性があります:





Nodeはどこでも、特にメインモジュールでイベントパターンを使用します。 したがって、イベントを正しく使用すれば、時間を大幅に節約できます。



ストリーム



Nodeで大量のデータを扱う場合、いくつかの問題があります。 パフォーマンスが低下する場合があり、バッファサイズは約1 GBに制限されます。 さらに、決して終わらないという期待を持って作成された無限のリソースで作業する方法は? このような状況では、ストリームが役立ちます。



Nodeのストリームは抽象化であり、データをフラグメントに連続的に分割することを示します。 つまり、リソースが完全にロードされるのを待つ必要はありません。 この図は、バッファリングの標準的なアプローチを示しています。





Node.jsのバッファリングアプローチ



データの処理や出力を開始する前に、バッファが完全にロードされるまで待つ必要があります。 次に、これをフローチャートと比較します。 この場合、最初のチャンクを取得するとすぐに、データの処理および/または出力をすぐに開始できます。





Node.jsでのストリームアプローチ



Nodeには4つのタイプのストリームがあります:





事実上、ストリームはNodeのあらゆる場所で使用されます。 最も人気のあるストリームの実装:





オブザーバーパターンを提供するために、ストリーム—イベント—は、イベントエミッタオブジェクトから継承します。 これを使用して、ストリームを実装できます。



読み取り可能なストリームの例



例は、標準入力ストリームであるprocess.stdin



です。 これには、アプリケーションに入るデータが含まれています。 これは通常、プロセスを開始するために使用されるキーボード情報です。



data



およびend



イベントは、 stdin



からデータを読み取るために使用されます。 data



イベントのコールバックには、引数としてchunk



があります。



 process.stdin.resume() process.stdin.setEncoding('utf8') process.stdin.on('data', function (chunk) { console.log('chunk: ', chunk) }) process.stdin.on('end', function () { console.log('--- END ---') })
      
      





次に、 chunk



入力としてプログラムに送られます。 このイベントは、着信情報の合計量に応じて、数回アクティブ化できます。 ストリームの完了は、 end



イベントを使用して通知する必要があります。



注: stdin



はデフォルトで一時停止されていますstdin



からデータを読み取る前にstdin



出力する必要があります。



読み取り可能なストリームには、同期的に動作するread()



インターフェイスがあります。 ストリームの最後で、 chunk



またはnull



返しnull



while



条件でnull !== (chunk = readable.read())



コンストラクトを設定することにより、この動作を利用できnull !== (chunk = readable.read())







 var readable = getReadableStreamSomehow() readable.on('readable', () => { var chunk while (null !== (chunk = readable.read())) { console.log('got %d bytes of data', chunk.length) } })
      
      





理想的には、スレッドのブロックを回避するために、できるだけ頻繁にNodeに非同期コードを記述したいと考えています。 ただし、チャンクのサイズが小さいので、スレッドをブロックする同期的readable.read()



を心配する必要はありません。



記録されたストリームの例



例は、標準出力ストリームであるprocess.stdout



です。 アプリケーションを離れるデータが含まれています。 write



操作を使用してストリームに書き込むことができます。



 process.stdout.write('A simple message\n')
      
      





console.log()



を使用したかのように、標準出力ストリームに記録されたデータがコマンドラインに表示されます。



パイプ



Nodeには、上記のイベントの代わりにpipe()



メソッドがあります。 次の例では、ファイルからデータを読み取り、GZipを使用して圧縮し、結果をファイルに書き込みます。



 var r = fs.createReadStream('file.txt') var z = zlib.createGzip() var w = fs.createWriteStream('file.txt.gz') r.pipe(z).pipe(w)
      
      





Readable.pipe()



はデータストリームを受け取り、すべてのストリームを通過するため、 pipe()



メソッドからチェーンを作成できます。



そのため、ストリームを使用する場合、イベントまたはパイプを使用できます。



HTTPストリーム



私たちのほとんどはNodeを使用してWebアプリケーションを作成します。従来の(サーバー側)またはREST APIに基づいています(クライアント側)。 HTTPリクエストはどうですか? ストリーミングできますか? 間違いなく!



要求と応答は、イベントエミッターから継承された読み取りおよび書き込みストリームです。 data



イベントレシーバーを接続し、コールバックでchunk



を受け入れることができます。 chunk



は、応答全体を待たずにすぐに変換できます。 次の例では、 body



を連結し、 end



イベントのコールバックに解析します。



 const http = require('http') var server = http.createServer( (req, res) => { var body = '' req.setEncoding('utf8') req.on('data', (chunk) => { body += chunk }) req.on('end', () => { var data = JSON.parse(body) res.write(typeof data) res.end() }) }) server.listen(1337)
      
      





注:ES6によると、 ()=>{}



は匿名関数の新しい構文であり、 const



は新しい演算子です。 ES6 / ES2015の機能と構文にまだ慣れていない場合は、 忙しいJavaScript開発者が知っておくべきES6プロパティに関するトップ10の記事ご覧ください



次に、Express.jsを使用して、サーバーを実際の生活から離さないようにします。 巨大な画像(約8 MB)と2組のExpressルート/stream



および/non-stream







server-stream.js:



 app.get('/non-stream', function(req, res) { var file = fs.readFile(largeImagePath, function(error, data){ res.end(data) }) }) app.get('/stream', function(req, res) { var stream = fs.createReadStream(largeImagePath) stream.pipe(res) })
      
      





また、イベントを使用した/stream2



代替実装と、 /non-stream2



同期実装もあります。 それらは同じことをしますが、異なる構文とスタイルを使用します。 この場合、複数の競合するリクエストではなく、1つのリクエストのみを送信するため、同期メソッドの方が高速です。



ターミナルからこのコードを実行できます。



 $ node server-stream
      
      





次に、Chrome http:// localhost:3000 / streamおよびhttp:// localhost:3000 / non-streamで開きますX-Response-Time



比較して、開発者ツールの[ネットワーク]タブのヘッダーに注目してください。 私の場合、 /stream



/stream2



は、桁違いに300ミリ秒異なりました。 3-5秒 値が異なる場合もありますが、考え方は明確です。/streamの場合/stream



ユーザー/クライアントはより早くデータを受信し始めます。 Nodeでのストリーミングは非常に強力なツールです! チームのこの分野の専門家になることで、ストリームリソースを適切に管理する方法を学ぶことができます。



npmを使用すると、 Stream Handbookstream-adventureをインストールできます。



 $ sudo npm install -g stream-adventure $ stream-adventure
      
      





バッファ



バイナリデータにはどの型を使用できますか? 覚えていれば、JavaScriptブラウザーにはバイナリデータ型はありませんが、Nodeにはあります。 これはバッファと呼ばれます。 これはグローバルオブジェクトなので、モジュールとしてインポートする必要はありません。



次の式のいずれかを使用して、バイナリタイプを作成できます。





メソッドとエンコーディングの完全なリストは、 バッファのドキュメントで入手できます 。 最も一般的に使用されるutf8



エンコーディング。



通常、バッファの内容はちらつきのように見えるので、人が読めるようにするには、まずtoString()



を使用して文字列表現に変換する必要があります。 for



ループを使用してアルファベットでバッファを作成for



ます。



 let buf = new Buffer(26) for (var i = 0 ; i < 26 ; i++) { buf[i] = i + 97 // 97 is ASCII a }
      
      





バッファを文字列表現に変換しない場合、数値の配列のようになります。



 console.log(buf) // <Buffer 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a>
      
      





変換を実行します。



 buf.toString('utf8') // outputs: abcdefghijklmnopqrstuvwxyz buf.toString('ascii') // outputs: abcdefghijklmnopqrstuvwxyz
      
      





文字列の一部(サブ文字列)のみが必要な場合、メソッドは目的のセグメントの初期番号と最終位置を取得します。



 buf.toString('ascii', 0, 5) // outputs: abcde buf.toString('utf8', 0, 5) // outputs: abcde buf.toString(undefined, 0, 5) // encoding defaults to 'utf8', outputs abcde
      
      





fs



覚えていますか? デフォルトでは、 data



もバッファーです。



 fs.readFile('/etc/passwd', function (err, data) { if (err) return console.error(err) console.log(data) });
      
      





data



は、ファイルを操作するときにバッファーとして機能します。



クラスター



Nodeの反対者は、スレッドが1つしかないため、スケーリングできるとしばしば主張します。 ただし、メインcluster



モジュールを使用すると(インストールする必要はありません。これはプラットフォームの一部です)、任意のマシンのすべてのプロセッサリソースを使用できます。 つまり、クラスターのおかげで、Nodeアプリケーションを垂直方向にスケーリングできます。



コードは非常に簡単です。モジュールをインポートし、1つのマスターと複数のワーカーを作成します。 通常、CPUごとに1つのプロセスを作成しますが、これは揺るぎない規則ではありません。 必要な数のプロセスを実行できますが、収益性を低下させる法律に従って、特定の時点から生産性の成長が停止します。



マスターコードと従業員コードは1つのファイルにあります。 従業員は、イベントをマスターに送信することにより、同じポートでリッスンできます。 ウィザードはイベントをリッスンし、必要に応じてクラスターを再起動できます。 ウィザードではcluster.isMaster()



れ、ワーカーでcluster.isMaster()



ます。 ほとんどのサーバーコードはワーカー( isWorker()



)に配置されます。



 // cluster.js var cluster = require('cluster') if (cluster.isMaster) { for (var i = 0; i < numCPUs; i++) { cluster.fork() } } else if (cluster.isWorker) { //    })
      
      





この例では、サーバーがプロセスIDを発行するため、さまざまなワーカーがさまざまな要求を処理する方法を確認できます。 ロードバランサーのように見えますが、負荷は均等に分散されないため、これは単なる印象です。 たとえば、PIDにより、プロセスの1つがより多くの要求を処理する方法を確認できます。



, , loadtest



Node:



  1. loadtest



    npm: $ npm install -g loadtest



  2. code/cluster.js



    node ( $ node cluster.js



    ); .
  3. : $ loadtest http://localhost:3000 -t 20 -c 10



    .
  4. loadtest



    .
  5. Ctrl+C. PID.


loadtest



-t 20 -c 10



, 10 20 .



— , . , . :





pm2



pm2



, Node-. , pm2



production.



pm2



:





https://github.com/Unitech/pm2 http://pm2.keymetrics.io .



pm2 - server.js



. , isMaster()



, , cluster



. pid



.



 var express = require('express') var port = 3000 global.stats = {} console.log('worker (%s) is now listening to http://localhost:%s', process.pid, port) var app = express() app.get('*', function(req, res) { if (!global.stats[process.pid]) global.stats[process.pid] = 1 else global.stats[process.pid] += 1; var l ='cluser ' + process.pid + ' responded \n'; console.log(l, global.stats) res.status(200).send(l) }) app.listen(port)
      
      





pm2 start server.js



. /, ( -i 0



, , , 4). -l log.txt



:



 $ pm2 start server.js -i 0 -l ./log.txt
      
      





, pm2 . :



 $ pm2 list
      
      





loadtest



, cluster



. :



 $ loadtest http://localhost:3000 -t 20 -c 10
      
      







, log.txt



- :



 cluser 67415 responded { '67415': 4078 } cluser 67430 responded { '67430': 4155 } cluser 67404 responded { '67404': 4075 } cluser 67403 responded { '67403': 4054 }
      
      





Spawn, Fork Exec



cluter.js



Node- fork()



. , Node.js : spawn()



, fork()



exec()



. child_process



. :





: node program.js



, — bash, Python, Ruby .. , , spawn()



. data



stream':



 var fs = require('fs') var process = require('child_process') var p = process.spawn('node', 'program.js') p.stdout.on('data', function(data)) { console.log('stdout: ' + data) })
      
      





node program.js



, data



, , .



fork()



spawn()



, : , fork()



, Node.js:



 var fs = require('fs') var process = require('child_process') var p = process.fork('program.js') p.stdout.on('data', function(data)) { console.log('stdout: ' + data) })
      
      





, exec()



. , , callback. error, standard output :



 var fs = require('fs') var process = require('child_process') var p = process.exec('node program.js', function (error, stdout, stderr) { if (error) console.log(error.code) })
      
      





error



stderr



, exec()



(, program.js



), — (, program.js



).





Node.js try/catch



. .



 try { throw new Error('Fail!') } catch (e) { console.log('Custom Error: ' + e.message) }
      
      





, . Java Node. Node.js , thread.



, , /. , .



, setTimeout()



, callback'. , HTTP-, :



 try { setTimeout(function () { throw new Error('Fail!') }, Math.round(Math.random()*100)) } catch (e) { console.log('Custom Error: ' + e.message) }
      
      





callback , try/catch



. , callback try/catch



, , . . try/catch



.



, . ? , callback' error



. : callback' .



  if (error) return callback(error) // or if (error) return console.error(error)
      
      





:





on('error')



on('error')



, Node.js, http



. error



, Express.js, LoopBack, Sails, Hapi .., http



.



 js server.on('error', function (err) { console.error(err) console.error(err) process.exit(1) })
      
      





uncaughtException



uncaughtException



process



! uncaughtException



— . , — Node.js — .



An unhandled exception means your application – and by extension Node.js itself – is in an undefined state. , .



 process.on('uncaughtException', function (err) { console.error('uncaughtException: ', err.message) console.error(err.stack) process.exit(1) })
      
      





または



 process.addListener('uncaughtException', function (err) { console.error('uncaughtException: ', err.message) console.error(err.stack) process.exit(1)
      
      





ドメイン



domain



. Node.js . , . : domain



callback' run()



:



 var domain = require('domain').create() domain.on('error', function(error){ console.log(error) }) domain.run(function(){ throw new Error('Failed!') })
      
      





4.0 domain



, Node . Node domain



. , , domain



npm-, npm. domain



.



setTimeout()



:



 // domain-async.js: var d = require('domain').create() d.on('error', function(e) { console.log('Custom Error: ' + e) }) d.run(function() { setTimeout(function () { throw new Error('Failed!') }, Math.round(Math.random()*100)) });
      
      





! domain



error



“Custom Error”, Node .



C++



Node , IoT, , , /++. /++ ?



. Node , ++! , .



hello.cc



, . , .



 #include <node.h> namespace demo { using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; using v8::Local; using v8::Object; using v8::String; using v8::Value; void Method(const FunctionCallbackInfo<Value>& args) { Isolate* isolate = args.GetIsolate(); args.GetReturnValue().Set(String::NewFromUtf8(isolate, "capital one")); // String } void init(Local<Object> exports) { NODE_SET_METHOD(exports, "hello", Method); // Exporting } NODE_MODULE(addon, init) }
      
      





, , , JavaScript. capital one



:



 args.GetReturnValue().Set(String::NewFromUtf8(isolate, "capital one"));
      
      





hello



:



 void init(Local<Object> exports) { NODE_SET_METHOD(exports, "hello", Method); }
      
      





hello.cc



, . binding.gyp



, :



 { "targets": [ { "target_name": "addon", "sources": [ "hello.cc" ] } ] }
      
      





binding.gyp



hello.cc



, node-gyp :



 $ npm install -g node-gyp
      
      





, hello.cc



binding.gyp



, :



 $ node-gyp configure $ node-gyp build
      
      





build



. build/Release/



.node



. , Node.js hello.js



, C++:



 var addon = require('./build/Release/addon') console.log(addon.hello()) // 'capital one'
      
      





capital one



, :



 $ node hello.js
      
      





C++ : https://github.com/nodejs/node-addon-examples .



おわりに



GitHub . Node.js, callback' Node-, Node: callback' observer' .



:



  1. : , / Node.
  2. : .
  3. : “observer” Node.js.
  4. : .
  5. : .
  6. : .
  7. Domain: .
  8. C++: .


Node JavaScript, , , , . , Node.js.



All Articles