記事の最後で同期と非同期について触れているものの、詳細は書いていないので、こちらでフォローしたい。まず「実は単に繋げるだけではうまくいかない。」この部分をテスト。
単純にMongoDBへのロードと検索を繋げると以下のようになるだろうか(以降、コピペで作動。ただしエラーチェックなどは含まず)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
var mongoClient = require('mongodb').MongoClient; var mongodb = 'mongodb://localhost:27017/test'; console.log('json read start'); mongoClient.connect(mongodb, function(err, db) { var http = require('http'); http.get('http://blog.iwh12.jp/test/tags.json', (json) => { var body = ''; json.setEncoding('utf8'); json.on('data', (chunk) => { body += chunk; }); json.on('end', () => { var d = JSON.parse(body); db.collection('tags').insertMany(d).then(function(err, r) { db.close(); console.log('json read end'); }); }); }); }); console.log('search start'); mongoClient.connect(mongodb, function(err, db) { db.collection('tags').find({}).toArray(function (err, tags) { console.log(tags); db.close(); console.log('search end'); process.exit(0); }); }); |
これを実行すると…。
$ node test1.js
json read start
search start
[]
search end
やはり’json read end’の文字がどこにも無く、検索結果は何も表示されない。これはMongoDBへのinsertMany()が非同期で行われるためで、「insertMany()宜しくね!」で丸投げして、結果を待たず先に進んでしまうからだ。
insertMany()が正常に終わった後、検索するには以下のようになる(見易いよう、検索側を関数にしている)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
var mongoClient = require('mongodb').MongoClient; var mongodb = 'mongodb://localhost:27017/test'; console.log('json read start'); mongoClient.connect(mongodb, function(err, db) { var http = require('http'); http.get('http://blog.iwh12.jp/test/tags.json', (json) => { var body = ''; json.setEncoding('utf8'); json.on('data', (chunk) => { body += chunk; }); json.on('end', () => { var d = JSON.parse(body); db.collection('tags').insertMany(d).then(function(err, r) { db.close(); console.log('json read end'); dbSearch(); }); }); }); }); function dbSearch() { console.log('search start'); mongoClient.connect(mongodb, function(err, db) { db.collection('tags').find({}).toArray(function (err, tags) { console.log(tags); db.close(); console.log('search end'); process.exit(0); }); }); } |
実行結果は、
$ node test2.js
json read start
json read end
search start
[ { _id: 58f29ed0b0e7000078daf12d,
id: 21,
count: 3,
description: ”,
link: ‘https://blog.iwh12.jp/tag/2in1/’,
name: ‘2in1’,
slug: ‘2in1’,
taxonomy: ‘post_tag’,
meta: [],
_links:
{ self: [Object],
collection: [Object],
about: [Object],
‘wp:post_type’: [Object],
curies: [Object] } },
.
. // 結果の表示
.
search end
と、’json read end’と結果が表示される。一般的に非同期で値を参照するには、コールバック内でしか有効とならず、(今回は1つだが)必要な処理が増えるほどネストも増えコードの見通しが悪くなる。
これを解決するには、本文でも触れたasyncモジュールを使う。npm install async −−saveでモジュールをインストールした上で、コードは以下のようになる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
// npm install async --save var async = require('async'); var mongoClient = require('mongodb').MongoClient; var mongodb = 'mongodb://localhost:27017/test'; async.series([ function (callback) { step1(function() { callback(); }); }, function (callback) { step2(function() { callback(); }); } ], function (err, results) { process.exit(0); }); function step1(callback) { console.log('json read start'); mongoClient.connect(mongodb, function(err, db) { var http = require('http'); http.get('http://blog.iwh12.jp/test/tags.json', (json) => { var body = ''; json.setEncoding('utf8'); json.on('data', (chunk) => { body += chunk; }); json.on('end', () => { var d = JSON.parse(body); db.collection('tags').insertMany(d).then(function(err, r) { db.close(); console.log('json read end'); callback(); }); }); }); }); } function step2(callback) { console.log('search start'); mongoClient.connect(mongodb, function(err, db) { db.collection('tags').find({}).toArray(function (err, tags) { console.log(tags); db.close(); console.log('search end'); callback(); }); }); } |
MongoDBへの読み込み(step1)と検索部分(step2)をどちらも関数にし、async.seriesへこのように書く。また各関数に引数callbackと、実際の処理終了部分にcallback();を追加。これにより、callback()が呼ばれるまで先に処理が進まず、見かけ上、同期作動となる。
全ての処理が終わるとfunction (err, results)の部分へ移り、(今回は)プログラムを終了。これなら非同期の数が増えてもコードの見通しが良くなるのがお分かり頂けだろうか。もちろん実行結果は二番目のコードと同じだ。
例えば、WordPress REST APIを使って記事を表示するには、該当するpost idを見つけ、その中にあるcategory idとtag idからカテゴリ名とタグ名を探し、前後の記事idを得るには、少なくともネストが4重になり、いろいろ処理を加えて行くとコードの見通しが非常に悪くなる。このような時に、asyncモジュールを使うと便利だ。
…と、言った内容を、PC Watchのあの後に追加するのは場違いのような気がしたので省略した次第。これで本当の(完)
余談になるが、macOSで構築している開発環境をBashへ移そうとしたところ、mount -t cifsやsshfsなどに対応しておらず、NASの共有フォルダがマウントできないことに気が付いた。RS2でやっとpingなどに対応したばかりなので、ある意味仕方ない部分だ。
ただ仕事場では案件ごとにフォルダ分けしてNASへ入れ、それをmacOSでmount、親フォルダをapacheのDocumentRootにしているが(Web系の内容が多いため)、Bashでは不可能。これでは開発環境を移行できない。
さらにPHPが7系。メンテナンスしている古いサイトはPHP 5.1とか普通にあり(笑)まずそのままでは動かない。このためバージョン違いのPHPをCGIで動かすなど、ちょっとした細工も必要となる。ここまでするならVMでLinuxを動かしても然程労力は変わらない感じがしないでもない。