node.js で何回かHTTP通信をしたときの対応。
node.jsはシングルスレッドで動作します。
さらにノンブロッキングなので同期的な処理は基本しません。
なのでHTTP通信を行って、結果を待って、次の処理というのは
普通に書くとネスト地獄になりコードの可読性が著しく悪くなります。
ということで今回は「Promise」を使用しました。
本当は「sync-request」を使おうと思ったのですが
非推奨?なのかあまり使うなと書かれていたので、今回はパス!
あと一応命名や文法も気にはしてますが、完全に守れてはいないのあしからず・・・
http://cou929.nu/data/google_javascript_style_guide/
http://popkirby.github.io/contents/nodeguide/style.html
何も考えないで実装した場合
const https = require ('https');
const url = require('url');
const ACCESS_URL = 'https://www.itcowork.co.jp/';
const opt = url.parse(ACCESS_URL);
opt.method = 'GET';
console.log('START');
// 変数を宣言
var code;
var body;
var req = https.request(opt, (res) => {
console.log('status code : ' + res.statusCode);
code = res.statusCode;
body = '';
res.on('data', (chunk) => {
body += chunk;
console.log('data');
});
res.on('end', () => {
console.log('end');
});
})
req.on('error', (err) => {
console.log('request error : ' + err.message);
});
req.end();
console.log('LAST CHECK : ' + code);
console.log('END');
START LAST CHECK : undefined END status code : 200 data end
先に「LAST CHECK」が出てあとからHTTP処理の結果が出ています。
1個だけHTTP処理をする分にはこれでも大丈夫です。
もしくは「end」の処理の部分に別Functionを呼び出してネストしまくるとか
でも一応は対応できます。
が、今回はもっとスマートに書きたいので「Promise」を使用します。
Promiseを利用した場合
const https = require ('https');
const url = require('url');
const ACCESS_URL = 'https://www.itcowork.co.jp/';
const opt = url.parse(ACCESS_URL);
opt.method = 'GET';
console.log('START');
test1().then((value) => {
console.log('status code : ' + value);
console.log('END');
}, (err) => {
console.error("error:", err.message);
});
function test1(){
return new Promise ((resolve, reject) => {
var req = https.request(opt, (res) => {
console.log('1.status code(function) : ' + res.statusCode);
var code = res.statusCode;
var body = '';
res.on('data', (chunk) => {
body += chunk;
console.log('1.data');
});
res.on('end', () => {
console.log('1.end');
resolve(code);
});
})
req.on('error', (err) => {
console.log('1.request error : ' + err.message);
reject(err)
});
req.end();
});
}
START 1.status code(function) : 200 1.data 1.end status code : 200 END
Promiseオブジェクトを作成して、thenで実行し実行結果を取得しました。
正しい順序でログが出てることがわかります。
複数回HTTP通信
次に複数回のHTTP通信の実装です。
お試しなのでHTTP通信先はまったく同じですごめんなさいm(_ _)m
const https = require ('https');
const url = require('url');
const ACCESS_URL = 'https://www.itcowork.co.jp/';
const opt = url.parse(ACCESS_URL);
opt.method = 'GET';
console.log('START');
var promise = Promise.resolve();
promise
.then(test1)
.then((result) => {
return new Promise(function(resolve, reject){
console.log("1.status code : " + result);
resolve(1);
});
})
.then(test2)
.then((result) => {
return new Promise(function(resolve, reject){
console.log("2.status code : " + result);
resolve(2);
});
})
.catch(onRejectted);
function test1(){
return new Promise ((resolve, reject) => {
var req = https.request(opt, (res) => {
console.log('1.status code(function) : ' + res.statusCode);
var code = res.statusCode;
var body = '';
res.on('data', (chunk) => {
body += chunk;
console.log('1.data');
});
res.on('end', () => {
console.log('1.end');
resolve(code);
});
})
req.on('error', (err) => {
console.log('1.request error : ' + err.message);
reject(err)
});
req.end();
});
}
function test2(){
return new Promise ((resolve, reject) => {
var req = https.request(opt, (res) => {
console.log('2.status code(function) : ' + res.statusCode);
var code = res.statusCode;
var body = '';
res.on('data', (chunk) => {
body += chunk;
console.log('2.data');
});
res.on('end', () => {
console.log('2.end');
resolve(code);
});
})
req.on('error', (err) => {
console.log('2.request error : ' + err.message);
reject(err)
});
req.end();
});
}
function onRejectted(error) {
console.log("error:" + error);
}
START 1.status code(function) : 200 1.data 1.end 1.status code : 200 2.status code(function) : 200 2.data 2.end 2.status code : 200
もし「test1」と「test2」のFunctionに引数を渡す場合は
以下のように呼び出せば正しく動作します。
var promise = Promise.resolve();
promise
.then((result) => {
return test1(1);
})
.then((result) => {
return new Promise(function(resolve, reject){
console.log("1.status code : " + result);
resolve(1);
});
})
.then((result) => {
return test2(2);
})
.then((result) => {
return new Promise(function(resolve, reject){
console.log("2.status code : " + result);
resolve(2);
});
})
.catch(onRejectted);
途中でエラーにしたい場合は以下のようにすればOKのはず
var promise = Promise.resolve();
promise
.then(test1)
.then((result) => {
return new Promise(function(resolve, reject){
console.log("1.status code : " + result);
if(result == 404) reject(new Error("わざとエラー"));
//resolve(1);
});
})
.then(test2)
.then((result) => {
return new Promise(function(resolve, reject){
console.log("2.status code : " + result);
resolve(2);
});
})
.catch(onRejectted);
START 1.status code(function) : 404 1.data 1.end 1.status code : 404 error:Error: わざとエラー
まとめ
久しぶりに複数処理を書きましたが、
非同期とか久しぶりすぎて忘れてました!
そして未だにアロー関数(=>)に慣れませんw
ちなみにソース中でHTTPアクセスしてるのは
前職のWEBサイトです。怒られたら変更しよう!
以上