概要
JavaScript(Node.js)でHTTPリクエストを送ったり、ファイルの読取を実装しようとすると、コールバックだらけになって、HTTPリクエストの後に行う処理が実装しづらかったりする。
その場合、async/awaitやPromiseを使って同期的にすることができる。基礎知識については、下記ページを参照。
async/await 入門(JavaScript) - Qiita
以下、Webサイトからcsvファイルを取得し、パースする処理を例にする。
コールバックで実装した場合
HTTPリクエストにhttps
、csvのパースにcsv-parse
を使う。
どちらもコールバック関数を使用する設計になっているため、ひたすら関数の呼び出しの連鎖が続き、処理の全体が把握しづらくなる。
コールバックを使用したサンプル
const https = require('https');
const parse = require('csv-parse');
const moment = require('moment');
(function() {
getCsv();
// ※ HTTPリクエストもcsvの処理も非同期なので、真っ先に出力されてしまうログ
console.log('finish');
}());
function getCsv() {
// csvファイル取得
const url = 'https://www.mizuhobank.co.jp/market/csv/quote.csv';
let csvdata = '';
https.get(url, function(res) {
res.on('data', function(data) {
csvdata += data;
});
res.on('end', function() {
// 次の処理を呼び出す
parseCsv(csvdata);
});
});
}
function parseCsv(csvdata) {
// csvファイルを配列へ変換する
let keepdata = [];
parse(csvdata, {
skip_empty_lines : true
,from_line : 4
})
.on('readable', function() {
let record = null;
while (record = this.read()) {
if (filterCsv(record)) {
keepdata.push(record);
}
}
})
.on('end', function() {
// 次の処理を呼び出す
fillEmptyRecord(keepdata);
});
}
function fillEmptyRecord(records) {
// 休日など、空いている日付を翌日のレートで埋める
let size = records.length;
let index = size - 1;
let newRecords = [];
while (index > 0) {
let date = moment(records[index][0], 'YYYY/M/D');
let prevDate = moment(records[index - 1][0], 'YYYY/M/D');
let diff = date.diff(prevDate, 'days');
newRecords.splice(0, 0, records[index]);
while (diff > 1) {
let cloned = records[index].slice(0, records[index].length);
date = date.subtract(1, 'days');
cloned[0] = date.format('YYYY/M/D');
newRecords.splice(0, 0, cloned);
diff -= 1;
}
index -= 1;
}
// ここが処理の一番最後
for (let record of records) {
console.log(record);
}
}
Promiseを使用した場合
先ほどの例では分かりづらい…という問題を解消するために、async/await や Promise を使用する。
こうするとエントリポイントとなる関数に処理の流れを表すことができるため、流れが把握しやすくなるし、実装もしやすい。
サンプル
const https = require('https');
const parse = require('csv-parse');
const moment = require('moment');
(async function() {
const csvdata = await getCsv();
const keepdata = await parseCsv(csvdata);
fillEmptyRecord(keepdata);
// ちゃんと最後に出力される
console.log('finish');
}());
function getCsv() {
// csvファイル取得
const url = 'https://www.mizuhobank.co.jp/market/csv/quote.csv';
let csvdata = '';
return new Promise(function(resolve, reject) {
https.get(url, function(res) {
res.on('data', function(data) {
csvdata += data;
});
// getが終わった後
res.on('end', function() {
resolve(csvdata);
});
res.on('error', function() {
reject(null);
});
});
});
}
function parseCsv(csvdata) {
let keepdata = [];
return new Promise(function(resolve, reject){
parse(csvdata, {
skip_empty_lines : true
,from_line : 4
})
.on('readable', function() {
// 略
})
.on('end', function() {
resolve(keepdata);
});
});
}
function fillEmptyRecord(records) {
// 中略
for (let record of records) {
console.log(record);
}
}