blog

NodeJsの基礎

簡単に言うと、Node.jsはサーバーサイドで動作します。 Node.js は Chrome V8 エンジンに基づいた実行環境です。 Node.jsは、GoogleのV8エンジンをベースにしたイベント...

Feb 15, 2020 · 11 min. read
シェア

NodeJS

簡単に言えば、Node.jsはサーバー上で動作するJavaScript。

Node.jsは、Chrome V8エンジンをベースにしたJavaScript実行環境です。Node.jsは、GoogleのV8エンジンをベースにしたイベント駆動型のI/OサーバーサイドJavaScript環境です。 V8エンジンはJavascriptを非常に高速に実行し、パフォーマンスも非常に優れています。

jsはインタプリタ言語ですが、コンパイルされるものもあります。

node.js

イベント駆動、非同期処理、ノンブロッキングI/Oなど、Node.jsのパワーは様々な形で反映されています。ここでは、Node.jsが持つ他のフレームワークとは異なる特徴を紹介します。

シングルスレッド

ここでシングルスレッドとは、メインスレッドが "シングルスレッド "であることを意味し、スレッドプールの一部のすべてのブロッキング部分に対処するために、その後、メインスレッドは、スレッドプールと協力するキューを介して、Node.jsは、シングルスレッドに基づいて、Node.jsの鍵は、軽量かつ高いパフォーマンスを維持することです。

シングルスレッド化の弱点:

  • マルチコアCPUを利用できず
  • あるユーザーがスレッドをクラッシュさせると、サービス全体がクラッシュします。
  • CPUを占有する重い計算により、非同期I/Oを呼び出し続けることが不可能に

シングルスレッドの利点

  • オペレーティングシステムは、スレッドの生成と破棄の時間的オーバーヘッドを完全に排除します。
  • メモリ・オーバーヘッドの削減、OSのメモリ・スワップ
  • マルチスレッドプログラミングのように、あらゆる場所での状態の同期を心配する必要はありません。
  • 40,000以上の同時ユーザー接続を処理できる8G RAMサーバー

非同期、ノンブロッキングI/O

ブロッキング ノンブロッキング

Jsはシングルスレッドですが、ブラウザはシングルスレッドではありません。もしそれがブロッキングであれば、スレッドはリクエストが完了するまで待ってから、別のリクエストのために解放されます。ノンブロッキングであれば、スレッドはリクエストの完了を待つことなく、リクエストを開始し、他の処理を続けることができます。

IOそれは何ですか?

I/O(入出力)とは、通常、内部メモリと外部メモリやその他の周辺機器との間でデータを入出力することを指します。

非同期IO操作

ファイルを読み取る方法は、一度にすべてを読み取ることですが、ファイルが大きすぎる場合、一度の読み取りは遅いだけでなく、ユーザーエクスペリエンスに影響を与えるので、ステップバイステップの読み取りを実現する方法。

これは非同期IOオペレーションを使って行わなければならず、セグメントを水の流れのように流して取得します。

具体的な実現:

ファイル読み取りストリームの作成、最初のコード

var fs = require("fs");
var data = ""; //読み取りデータを保持する空の文字列を宣言する
var rs = fs.createReadStream("a.txt"); //読みやすいストリームを作る
rs.setEncoding("utf-8");
//データが入ってくるタイミングを聞く
rs.on("data",function(chunc){
 data += chunc; //読み込んだデータをデータに継ぎ足す。
 console.log("..."); //読書中に3つの点を印刷する
});
rs.on("end",function(){
 console.log("もうデータはいらない")
});

reateReadStreamを使用して読み取りストリームオブジェクトを作成し、onを使用してオブジェクトの "data "読み取りデータイベントをリッスンします。イベントが発生します。

実行後、複数の"... "行の印刷から、下に印刷された結果を見ることができます。印刷された複数の"... "行から、全体を読むのに何回もかかったことがわかります。

はデータを読み込み、ゆっくりとb.txtに書き込みます。

 var fs = require("fs"); 
 var rs = fs.createReadStream("a.txt");//読みやすいストリームを作る
 var ws = fs.createWriteStream("b.txt"); //ライトストリームを作る
 rs.setEncoding("utf-8");
 //データが入ってくるタイミングを聞く
 rs.on("data",function(chunc){
 console.log("..."); //読書中に3つの点を印刷する
 ws.write(chunc,"utf-8"); //ファイルに書き込む
 });
 rs.on("end",function(){
 console.log("もうデータはいらない");
 ws.end(); //書き込みストリームをオフにする
 });

このようにして、ファイルの非同期読み書きが実装されます。

非同期I/O機構は、ので、データベースにアクセスするコードの実行後、すぐにコールバック関数内の処理コードの結果を返すために、データベースは、I/Oコールの終了を待たずに、各呼び出しの間に、その背後にあるコードの実行に行くプログラムの実行効率を向上させます。

非同期I/Oの大まかな流れ:

I/Oコールの開始

  • ユーザはJavaScriptコードを介してNodeコアモジュールを呼び出し、コアモジュールにパラメータとコールバック関数を渡します。
  • Node コアモジュールは入力パラメータとコールバック関数をリクエストオブジェクトにカプセル化します。
  • このリクエストオブジェクトをI/Oスレッドプールにプッシュし、実行を待ちます。
  • JavaScriptが開始した非同期呼び出しは終了し、JavaScriptスレッドは後続の処理を継続します。

コールバックの実行

  • I/O操作が完了すると、リクエストオブジェクトのresult属性に結果が格納され、操作完了の通知が発行されます。
  • イベントループは毎回、完了したI/O操作があるかどうかをチェックし、もしあればリクエストオブジェクトをオブザーバーキューに追加し、その後イベントとして処理します。
  • I/Oオブザーバーイベントの処理は、リクエストオブジェクトにカプセル化されたコールバック関数を取り出し、このコールバック関数を実行し、JavaScriptコールバックを完了するために結果を引数として受け取ります。
var fs=require('fs');
fs.readFile('/path',function(err,file){
 console.log('ファイルの読み込みが完了した')
});

ここで、"initiate file read "は "read file complete "の後に出力され、同様に、"read file complete "の実行は、ファイルを読むための非同期呼び出しがいつ終了するかに依存します。

イベントとコールバック関数

node.jsでは、イベントコールバック関数は一瞬しか実行できませんが、イベントコールバック関数を実行する過程で、他の処理に切り替えて、また元のイベントコールバック関数の実行に戻ることができます。

イベント・ドリブンの利点は、システム・リソースをフルに活用し、操作の完了を待つことなくコードを実行し、限られたリソースを他のタスクに使用できることです。Node.jsの目的はバックエンドのWebサービスをプログラムすることであり、サーバー開発では同時リクエスト処理が大きな問題となり、ブロック関数はリソースの浪費や時間遅延につながります。イベントや非同期関数を登録することで、開発者はリソースの利用率を高め、パフォーマンスを向上させることができます。

準備

util.promisify マイエクスプレスの近況

util.promisifyはnode.jsバージョン8.xの新しいツールで、昔ながらのコールバックをPromiseオブジェクトに変換し、古いプロジェクトの後付けを簡単にします。

このツールが正式に発表される以前から、es6-promisify、thenify、bluebird.promisifyなど、類似のツールが数多く存在しています。

プロミシファイを手動で実装

function getName() {
 let p
 p = new Promise(function (resolve, reject) {
 setTimeout(function() {
 resolve('jianyong')
 }, 2000)
 })
 return p
}
getName().then(function (name) {
 console.log(name)
})

nodejsでは、多くの関数が非同期実行であるため、非同期関数の最後のパラメータとしてコールバック関数を記述する必要があります。例えば、fsパッケージのreadFileなどは非同期関数です。

 //リードを同期させる
 const data = fs.readFileSync('./package.json');
 console.log('data',data);
 console.log('data2',data.toString());
 //非同期リーディング
 const data2 = fs.readFile('./package.json',function(err,data){
 console.log('非同期データ,data);
 console.log('非同期データ2',data.toString());
 });

プロミスでfsを変換.readFile

 // プロミス」を使ってfsを変換する.readFile
 function readfile(file) { 
 return new Promise(function (resolve, reject) { 
 fs.readFile(file, function (err, data) {
 if (err) { reject(err) } else { resolve(data) }
 }) 
 }) 
 } 
 readfile('./package.json').then(function (data) { console.log(data.toString()) })
})();

非同期操作に遭遇するたびに、それをラップして上の例のように変換する必要があるとしたら、面倒ではありませんか?

この変換は、コリエリゼーション機能を使って続けることができます:

 function promisify(fn) {
 return function () {
 // let args = Array.prototype.slice.call(arguments);
 let args = Array.from(arguments);
 console.log(args)
 //------ 最初のパラメーターは'./package.json' , は第2引数にコールバックを指定する。 
 // function (err, data) {
 // if (err) { 
 // reject(err) 
 // } else { 
 // resolve(data)
 // }
 // }) 
 
 return new Promise(function (resolve, reject) {
 args.push(function (err, result) {
 if (err) { reject(err); }
 else resolve(result);
 });
 // 次のようにしたい。[0],args[1]) 
 fn.apply(null, args);
 });
 }
 }
 //  
 var readFile = promisify(fs.readFile);
 readFile('./package.json').then((res)=>{
 console.log(res.toString())
 })

組み込みのpromisifyポストコンバージョン機能

Nodeのバージョンが10.x以上であれば、.promiseに似たサブモジュールを多くの組み込みモジュールで見つけることができます。fs モジュールを例にとってみましょう:

// fs関連のAPIをいくつか紹介する。
const { readFile, stat } = require('fs')
// を単純に
const { readFile, stat } = require('fs').promises
//  
const { promises: { readFile, stat } } = require('fs')
readFile('./package.json').then((res)=>{
    console.log('コールバック関数からのデータ:',res.toString());
})

fs組み込みモジュール:I/O操作の実装

  • fs.mkdir / fs.mkdirSync:フォルダの作成は、同期作成であり、その逆は非同期ではありませんが、非ブロッキングI / O操作を実現したい、一般的に処理することを完了するために非同期操作を使用します。
  • fs.readdir / fs.readdirSync:ファイルディレクトリの内容を読み込む
  • fs.rmdir フォルダの削除
  • fs.readFile: ファイルの内容を読み込む
  • fs.writeFile: コンテンツをファイルに書き込みます。
  • fs.appendFile:新しいコンテンツを追加します。
  • fs.copyFile:ファイルを新しい場所にコピーします。
  • fs.unlinkファイルの削除

モジュールとの連携

node 組み込みモジュール require('os') サードパーティモジュール、cpu-stat をインストールする必要あり require("cpu-stat"); カスタムモジュール: // export module.exports = {} // import require('./conf')

Buffer

http

NodeのWebアプリケーションでは、Webサービスオブジェクトを作成する必要があります。createServer に渡された関数は HTTP リクエストごとに呼び出されるので、この関数はリクエストハンドラとしても知られています。createServerによって返されるServerオブジェクトは、実際にはEventEmitterです。

var http = require('http');
var server = http.createServer(function(request, response) {   
// handle your request
});

そういうこともあります:

var http = require('http');
var server = http.createServer(); 
server.on('request', function(request, response) {
// handle your request
});

このサービスに対してHTTPリクエストが行われると、nodeはリクエストハンドラ関数を呼び出し、いくつかのトランザクション関連オブジェクトを渡します。

var http = require('http');
var server = http.createServer(); 
server.on('request', function(request, response) {
 // handle your request
}).listen(8080); 

実際のリクエストを処理するには、サーバオブジェクトの listen メソッドをコールする必要があります。たいていの場合は listen メソッドにサービスに listen させたいポート番号を渡します。

EventEmitter

events モジュールが提供するオブジェクトは、events.EventEmitter だけです。 EventEmitter は、イベントのトリガーとイベントリスナーの機能をカプセル化したものです。このモジュールには require("events"); でアクセスできます。どちらもリスナーにバインドされ、eventEmitter.on()はイベントをリッスンするために使用され、eventEmitter.emit()はイベントをトリガーするために使用されます。

EventEmitter の継承

ほとんどの場合、EventEmitter を直接使用するのではなく、オブジェクトから継承します。fs、net、http を含む、イベント・レスポンスをサポートする全てのコア・モジュールは、EventEmitter のサブクラスです。なぜこのようなことをするのでしょうか?理由は2つあります:

  • あるエンティティの機能を持つオブジェクトがイベントを実装するのは意味的に正しく、イベントのリスニングと発生はオブジェクトのメソッドであるべきです。
  • JavaScript のオブジェクト機構はプロトタイプをベースにしており、部分的な多重継承をサポートしています。 EventEmitter を継承しても、オブジェクトの元の継承関係が崩れることはありません。

EventEmitterクラスの継承

問題 ある問題をイベント・ドリブンで解決したいとします。非同期イベントまたはインターフェイスが成功したときに、それを操作したいとします。

解決方法 EventEmitterクラスに基づいてカスタムクラスを作成する必要があります。 EventEmitterクラスに基づいて取得した例は、リスナーにバインドされています。eventEmitter.on()はイベントをリッスンするために使用され、eventEmitter.emit()はイベントをトリガーするために使用されます。

まず、EventEmitterクラスの継承を実装します。

const EventEmitter = require('events');
//全てのコンストラクタはEventEmitterクラスを継承しなければならない。;
class MusicPlayer extends EventEmitter{};
//このコンストラクターは、イベントをトリガーするオブジェクトを作成する。
let musicPlayer = new MusicPlayer();
// 継承によって作られたインスタンス・オブジェクトは、バインディング・リスナーを持ち、on,emitメソッドを呼び出すことができる。
// 購読する 発行する
//オーディオ機器 
let AudioDevice = {
 play:function(track){
 //
 console.log(track);
 console.log('audio play');
 },
 stop:function(){
 //
 console.log('audio stop');
 }
 }
 //イベントを聴く
 musicPlayer.on('play',function(track){
 this.playing = true;
 AudioDevice.play(track);
 })
 //イベントを聴く
 musicPlayer.on('stop',function(track){
 this.playing = false;
 AudioDevice.stop();
 });
 
 musicPlayer.emit('play','The Roots - The Fire');
 
 setTimeout(function(){
 //emitイベントを誘発する
 musicPlayer.emit('stop')
 },3000);

複数リスナーの追加

上記の音楽プレーヤーのように、再生がトリガーされた時にユーザーインターフェイスを更新する必要があるなど、別の処理を行う必要があるイベントに複数のリスナーを追加することが可能です。playイベントに新しいリスナーを追加することで、簡単にできます。

musicPlayer.on('play',function(track){
 this.playing = true;
 AudioDevice.play(track);});//新しいリスナーを追加する
musicPlayer.on('play',function(track){
 console.log('新しいリスナーを追加する)});

リスナーの削除

eventEmitter.removeListener(eventname,fn):リスナーを削除する
emitter.removeAllListeners([eventName]):すべてのリスナーを削除する
let playFn1 = function(track){
 this.playing = true;
 AudioDevice.play(track);}
musicPlayer.on('play',playFn1);//リスナーを削除する
musicPlayer.removeEventListener('play',playFn1())

エラー処理

エラー処理は、エラーイベントをリッスンすることで行われます。

//エラー処理
let playFn1 = function(track){
 this.playing = true;
 AudioDevice.play(track);
 //ここでエラーが発生すると、エラーイベントが発生する。
 this.emit('error','unable to play')}
musicPlayer.on('play',playFn1);
musicPlayer.on('error',function(err){
console.log(err);})

拡張: Vue親子コンポーネント通信 $emit

トリガーを発行するには、親コンポーネントのv-on: / @カスタムイベントリスナーを使用します。

stream

nodejs.org/en/

エクスプレスのショートバージョンを書く

エクスプレス体験

const express = require('express');
const app = express();
app.get('/',(req,res) => {
 res.end('Hello world')
})
app.get('/users',(req,res) => {
 res.end(JSON.stringify({name:'abc'}))
})
app.listen(3100 , () => {
 console.log('Example listen at 3000')
})

マイエクスプレスの実装

const http = require('http');
const url = require('url');
let routers = [];
class Application {
 get(path,hander){
 routers.push({
 path,
 method:'get',
 hander
 });
 }
 listen2(){
 const server = http.createServer(function(req,res){
 const {pathname} = url.parse(req.url,true);
 const {method,headers} = req;
 console.log(method);
 //ルーティングテーブル・ルーターからパス名でコールバックを探し、それを実行する。 
 var tet = routers.find(v=>{
 return v.path == pathname && req.method.toLowerCase()==v.method
 })
 tet && tet.hander(req,res); 
 
 })
 //アプリケーション・プロトタイプにlistenメソッドを追加し、パスに一致させ、対応するハンドラを実行する。
 
 server.listen(...arguments)
 // server.listen(3200,() => {
 // console.log('Example listen at 3200')
 // })
 
 }
 
}
module.exports = function(){
 return new Application();
}
Read next

deferとasyncの設定が私のスクリプトとどう関係があるのか?

asyncはスクリプトの非同期実行を指定します。 deferはページがロードされるまでスクリプトの実行を延期するかどうかを指定します。" W3school: tags.

Feb 15, 2020 · 5 min read